From 4f21079376ff53cea45458704ab074afc95d5ecb Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:07:10 -0700 Subject: [PATCH 01/97] fix: preserve hub candidates and bounded weighted trails --- CHANGELOG.md | 9 ++ COMPATIBILITY.md | 23 +++ crates/compass-cli/tests/code_query_cli.rs | 73 ++++++++++ crates/compass-graph/src/analyze.rs | 79 ++++------- .../compass-graph/tests/analyze_coverage.rs | 98 +++++++++++++ crates/compass-mcp/src/lib.rs | 56 ++++++++ crates/compass-query/src/code_query.rs | 65 ++++++--- crates/compass-query/tests/code_traversal.rs | 134 +++++++++++++++++- docs/reference/commands.md | 5 + docs/reference/outputs.md | 3 +- 10 files changed, 471 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2f6bbdf1..d981d2613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +- Fix bounded weighted node trails: retain shorter prefixes when a cheaper + route exhausts the hop limit, and never admit a previously rejected node + without charging the traversal budget. + +- Respect explicit graph node kinds in topology analysis: method-shaped labels + remain callable candidates, and explicitly typed files stay out of hub lists. + +- Make god-node ranking stable for equal-degree nodes, retain project + declarations whose names overlap library names, and omit isolated nodes. - Move theme selection into Graph settings, grouped with appearance controls. Separate layout and selection settings, collapse keyboard shortcuts, and keep theme settings available in every community overview design. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 7d16f5572..68a34f035 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -94,6 +94,29 @@ history profiles, and cache identities. ## Evolving contracts +### Bounded node trails + +Typed `node`/node-trail queries keep nondominated arrivals by node and depth, +so a cheaper but longer prefix cannot hide a valid trail within `max_depth`. +Rejected nodes are not considered admitted on a later visit. Existing cost +weights, direction, deterministic tie rules, work limits and response schema +remain unchanged. Previously missing trails can now be returned; incomplete +responses no longer include nodes admitted after their budget was exhausted. + +### Hub ranking + +New god-node analyses order equal-degree candidates by stable node ID instead +of input record order. Source-located declarations named `Path`, `Counter`, +`Enum`, or other names also used by libraries are no longer suppressed by name +alone. Explicit canonical node kinds take precedence over display-label +heuristics throughout topology analysis: a `.method()` label does not turn a +method into a file, and file nodes remain excluded even with descriptive labels. +Legacy records without a recognized kind retain the existing label fallback. +Isolated declarations are omitted. The serialized `id`, `label`, and +`degree` fields and degree calculation are unchanged; the candidate list can +change. Published historical artifacts are not rewritten. Hub rank describes +connectivity, not a verified god-object design defect. + Rust structural evidence now uses producer version 2. The evidence and graph schema majors are unchanged, but Rust extraction caches from producer version diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 2ba3db581..60d5a380f 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -101,6 +101,79 @@ fn typed_query_commands_share_the_versioned_json_contract() -> Result<(), Box Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = support::write_typed_graph(directory.path())?; + let mut graph = GraphDocument::load(&graph_path)?; + let node = graph + .nodes + .first() + .cloned() + .ok_or("missing node template")?; + let edge = graph + .links + .first() + .cloned() + .ok_or("missing edge template")?; + graph.nodes = ["s", "a", "b", "t"] + .into_iter() + .map(|name| { + let mut record = node.clone(); + record.id = format!("n:{name}"); + record.name = name.to_owned(); + record.qualified_name = name.to_owned(); + record + }) + .collect(); + graph.links = [ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:a", EdgeKind::Calls, "n:b"), + ("n:s", EdgeKind::References, "n:b"), + ("n:b", EdgeKind::Calls, "n:t"), + ] + .into_iter() + .map(|(source, kind, target)| { + let mut record = edge.clone(); + record.source = source.to_owned(); + record.target = target.to_owned(); + record.kind = kind; + record.id = compass_model::identity::edge_id( + source, + kind, + target, + record.relationship_site.as_ref(), + None, + ); + record.key.clone_from(&record.id); + record + }) + .collect(); + std::fs::write(&graph_path, serde_json::to_vec(&graph)?)?; + let outcome = run( + Frontend::Compass, + [ + OsString::from("node"), + OsString::from("n:s"), + OsString::from("n:t"), + OsString::from("--graph"), + graph_path.into_os_string(), + OsString::from("--cache"), + directory.path().join("cache").into_os_string(), + OsString::from("--max-depth"), + OsString::from("2"), + OsString::from("--format"), + OsString::from("json"), + ], + ); + assert_eq!(outcome.code, 0, "{}", outcome.stderr); + let response: compass_model::query_contract::CodeQueryResponse = + serde_json::from_str(&outcome.stdout)?; + assert_eq!(response.paths.len(), 1); + assert_eq!(response.paths[0].node_ids, ["n:s", "n:b", "n:t"]); + Ok(()) +} + #[test] fn affected_typed_graph_uses_shared_relationship_output_contract() -> Result<(), Box> { let directory = tempfile::tempdir()?; diff --git a/crates/compass-graph/src/analyze.rs b/crates/compass-graph/src/analyze.rs index cb0aba675..0db078d7d 100644 --- a/crates/compass-graph/src/analyze.rs +++ b/crates/compass-graph/src/analyze.rs @@ -3,60 +3,13 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; use ahash::{AHashMap as HashMap, AHashSet as HashSet}; use std::path::Path; -use compass_model::{EdgeRecord, GraphDocument, NodeRecord}; +use compass_model::{EdgeRecord, GraphDocument, NodeRecord, code_graph::NodeKind}; use rayon::prelude::*; use serde::Serialize; use sha2::{Digest, Sha256}; use crate::cluster::{Communities, PythonRandom, score_communities}; -const BUILTIN_NOISE_LABELS: &[&str] = &[ - "str", - "int", - "float", - "bool", - "bytes", - "bytearray", - "complex", - "object", - "True", - "False", - "MagicMock", - "Mock", - "AsyncMock", - "NonCallableMock", - "NonCallableMagicMock", - "PropertyMock", - "patch", - "sentinel", - "Path", - "Any", - "Optional", - "List", - "Dict", - "Set", - "Tuple", - "Union", - "Callable", - "Type", - "ClassVar", - "Final", - "Literal", - "Protocol", - "Counter", - "defaultdict", - "OrderedDict", - "datetime", - "Enum", - "os", - "sys", - "re", - "json", - "io", - "abc", - "typing", -]; - const JSON_NOISE_LABELS: &[&str] = &[ "start", "end", @@ -255,6 +208,11 @@ pub struct ImportCycle { } #[must_use] +/// Return connected, source-located hub candidates, ordered by degree then ID. +/// +/// This is a topology ranking, not proof of a god-object design problem. A +/// project's declaration must not be discarded just because its name is also +/// used by a standard library or test framework. pub fn god_nodes(document: &GraphDocument, top_n: usize) -> Vec { let graph = AnalysisGraph::new(document); god_nodes_in(&graph, top_n) @@ -267,14 +225,19 @@ fn god_nodes_in(graph: &AnalysisGraph<'_>, top_n: usize) -> Vec { .enumerate() .map(|(position, node)| (position, node, graph.degree(position))) .collect::>(); - ranked.sort_by(|left, right| right.2.cmp(&left.2).then_with(|| left.0.cmp(&right.0))); + ranked.sort_by(|left, right| { + right + .2 + .cmp(&left.2) + .then_with(|| left.1.id.cmp(&right.1.id)) + }); ranked .into_iter() - .filter(|(position, node, _)| { - !graph.is_file_node(*position) + .filter(|(position, node, degree)| { + *degree > 0 + && !graph.is_file_node(*position) && !is_concept_node(node) && !is_json_key_node(node) - && !BUILTIN_NOISE_LABELS.contains(&node.label()) }) .take(top_n) .map(|(_, node, degree)| GodNode { @@ -1773,6 +1736,11 @@ impl<'a> AnalysisGraph<'a> { } fn is_file_node(&self, node: usize) -> bool { let record = self.nodes[node]; + // Canonical kinds are stronger evidence than display labels. In + // particular, `.method()` is a callable label, not a file identity. + if let Some(kind) = explicit_node_kind(record) { + return kind == NodeKind::File; + } let label = record.label(); if label.is_empty() { return false; @@ -1827,6 +1795,13 @@ fn invert_communities(communities: &Communities) -> HashMap { .collect() } +fn explicit_node_kind(node: &NodeRecord) -> Option { + serde::Deserialize::deserialize( + serde::de::value::StrDeserializer::::new(node.kind_name()), + ) + .ok() +} + fn is_concept_node(node: &NodeRecord) -> bool { let source = attribute(node, "source_file").unwrap_or_default(); source.is_empty() || !source.rsplit('/').next().unwrap_or_default().contains('.') diff --git a/crates/compass-graph/tests/analyze_coverage.rs b/crates/compass-graph/tests/analyze_coverage.rs index 943358cb6..93010f148 100644 --- a/crates/compass-graph/tests/analyze_coverage.rs +++ b/crates/compass-graph/tests/analyze_coverage.rs @@ -42,6 +42,104 @@ fn edge(source: &str, target: &str, relation: &str, confidence: &str) -> Value { }) } +#[test] +fn god_nodes_preserve_project_declarations_named_like_builtins() { + let graph = document( + vec![ + node("path", "Path", "src/path.rs"), + node("counter", "Counter", "src/counter.py"), + node("service", "Service", "src/service.rs"), + node("external", "Path", ""), + ], + vec![ + edge("path", "counter", "uses", "EXTRACTED"), + edge("path", "service", "calls", "EXTRACTED"), + edge("path", "external", "references", "EXTRACTED"), + ], + true, + ); + let ranked = god_nodes(&graph, 10); + assert_eq!( + ranked + .iter() + .map(|node| node.id.as_str()) + .collect::>(), + ["path", "counter", "service"] + ); + assert_eq!(ranked[0].degree, 3); + assert!(god_nodes(&graph, 0).is_empty()); +} + +#[test] +fn god_nodes_use_explicit_kinds_instead_of_method_label_heuristics() { + let graph = document( + vec![ + json!({"id":"method", "kind":"method", "name":".dispatch()", + "source":{"file":"src/service.rs", "startLine":5}}), + json!({"id":"function", "kind":"function", "name":"helper()", + "source":{"file":"src/service.rs", "startLine":20}}), + json!({"id":"file", "kind":"file", "name":"Service implementation", + "source":{"file":"src/service.rs", "startLine":1}}), + json!({"id":"caller", "kind":"class", "name":"Caller", + "source":{"file":"src/caller.rs", "startLine":1}}), + ], + vec![ + edge("method", "function", "calls", "EXTRACTED"), + edge("method", "caller", "references", "EXTRACTED"), + edge("file", "method", "contains", "EXTRACTED"), + edge("file", "caller", "references", "EXTRACTED"), + ], + true, + ); + let ranked = god_nodes(&graph, 10); + assert_eq!( + ranked + .iter() + .map(|node| node.id.as_str()) + .collect::>(), + ["method", "caller", "function"] + ); + assert_eq!(ranked[0].degree, 3); +} + +#[test] +fn god_nodes_do_not_label_isolated_declarations_as_hubs() { + let graph = document( + vec![node("isolated", "Service", "src/service.rs")], + Vec::new(), + true, + ); + assert!(god_nodes(&graph, 10).is_empty()); +} + +#[test] +fn god_nodes_ties_are_stable_under_graph_record_permutations() { + let mut graph = document( + vec![ + node("z", "Zulu", "src/z.rs"), + node("a", "Alpha", "src/a.rs"), + node("b", "Beta", "src/b.rs"), + ], + vec![ + edge("z", "a", "calls", "EXTRACTED"), + edge("a", "b", "calls", "EXTRACTED"), + edge("b", "z", "calls", "EXTRACTED"), + ], + true, + ); + let first = god_nodes(&graph, 2); + graph.nodes.reverse(); + graph.links.reverse(); + assert_eq!(first, god_nodes(&graph, 2)); + assert_eq!( + first + .iter() + .map(|node| node.id.as_str()) + .collect::>(), + ["a", "b"] + ); +} + #[test] fn questions_cover_no_signal_isolation_inference_ambiguity_bridge_and_low_cohesion() -> Result<(), Box> { diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index dc58419bc..62dd5eb61 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -3185,6 +3185,62 @@ mod tests { Ok(()) } + #[test] + fn god_nodes_tool_keeps_project_names_and_orders_equal_degrees_by_id() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let graph = temp.path().join("graph.json"); + fs::write( + &graph, + serde_json::to_vec(&json!({ + "directed": true, + "nodes": [ + {"id":"z", "label":"Path", "source_file":"src/path.rs"}, + {"id":"a", "label":"Counter", "source_file":"src/counter.rs"}, + {"id":"isolated", "label":"Unused", "source_file":"src/unused.rs"} + ], + "links": [{"source":"z", "target":"a", "relation":"uses"}] + }))?, + )?; + let server = CompassMcp::new(&graph); + assert_eq!( + server.invoke("god_nodes", Map::new()), + "God nodes (most connected):\n 1. Counter - 1 edges\n 2. Path - 1 edges" + ); + let arguments = Map::from_iter([("top_n".to_owned(), json!(1))]); + assert_eq!( + server.invoke("god_nodes", arguments), + "God nodes (most connected):\n 1. Counter - 1 edges" + ); + Ok(()) + } + + #[test] + fn god_nodes_tool_respects_explicit_method_and_file_kinds() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let graph = temp.path().join("graph.json"); + fs::write( + &graph, + serde_json::to_vec(&json!({ + "directed": true, + "nodes": [ + {"id":"method", "kind":"method", "name":".dispatch()", + "source":{"file":"src/service.rs", "startLine":5}}, + {"id":"file", "kind":"file", "name":"Service implementation", + "source":{"file":"src/service.rs", "startLine":1}} + ], + "links": [{"source":"file", "target":"method", "relation":"contains"}] + }))?, + )?; + let server = CompassMcp::new(&graph); + assert_eq!( + server.invoke("god_nodes", Map::new()), + "God nodes (most connected):\n 1. .dispatch() - 1 edges" + ); + Ok(()) + } + #[test] fn every_local_tool_and_resource_handles_success_missing_and_filter_shapes() -> Result<(), Box> { diff --git a/crates/compass-query/src/code_query.rs b/crates/compass-query/src/code_query.rs index 89c2eddc1..9236a2edd 100644 --- a/crates/compass-query/src/code_query.rs +++ b/crates/compass-query/src/code_query.rs @@ -34,6 +34,9 @@ use crate::text::{canonical_query_token, query_recall_terms, strip_diacritics}; type GraphPath = (Vec, Vec); type BoundedPathResult = (Option, bool); +// A cheaper arrival at a node may consume more hops. Retain separate states +// so it cannot erase a costlier arrival that still fits the remaining depth. +type TrailState = (String, usize); const MAX_CODE_QUERY_CANDIDATES: u32 = 256; const MAX_RECALL_FUZZY_VARIANTS_PER_TERM: usize = 192; const MAX_RECALL_FUZZY_VARIANTS_TOTAL: usize = 256; @@ -3458,28 +3461,37 @@ impl CodeQueryEngine { source.to_owned(), source.to_owned(), ))]); - let mut best = HashMap::from([(source.to_owned(), (0_u32, 0_usize, source.to_owned()))]); + let mut best = HashMap::from([( + source.to_owned(), + BTreeMap::from([(0_usize, (0_u32, source.to_owned()))]), + )]); let mut admitted = HashSet::from([source.to_owned()]); - let mut predecessor = HashMap::::new(); + let mut predecessor = HashMap::::new(); let mut truncated = false; while let Some(Reverse((cost, depth, path_key, node))) = queue.pop() { self.check_deadline()?; - if best.get(&node).is_none_or(|current| { - current.0 != cost || current.1 != depth || current.2 != path_key - }) { + let state = (node.clone(), depth); + let Some(labels) = best.get(&node) else { + continue; + }; + if labels + .get(&depth) + .is_none_or(|current| current.0 != cost || current.1 != path_key) + || labels.range(..depth).any(|(_, current)| current.0 <= cost) + { continue; } if node == target { let mut nodes = vec![target.to_owned()]; let mut edges = Vec::new(); - let mut cursor = target; - while cursor != source { - let Some((previous, edge)) = predecessor.get(cursor) else { + let mut cursor = state; + while cursor.0 != source || cursor.1 != 0 { + let Some((previous, edge)) = predecessor.get(&cursor) else { return Ok((None, truncated)); }; edges.push(edge.clone()); - nodes.push(previous.clone()); - cursor = previous; + nodes.push(previous.0.clone()); + cursor = previous.clone(); } nodes.reverse(); edges.reverse(); @@ -3524,19 +3536,32 @@ impl CodeQueryEngine { let next_depth = depth.saturating_add(1); let next_cost = cost.saturating_add(code_relation_weight(edge.kind)); let next_key = format!("{path_key}\0{}\0{next}", edge.id); - let candidate = (next_cost, next_depth, next_key.clone()); - if best - .get(&next) - .is_some_and(|current| candidate >= current.clone()) - { + let next_state = (next.clone(), next_depth); + let candidate = (next_cost, next_key.clone()); + // A route can dominate another only if it is no deeper and + // no costlier. In particular, reject positive-cost cycles. + if best.get(&next).is_some_and(|labels| { + labels.range(..=next_depth).any(|(known_depth, current)| { + current.0 < next_cost + || (current.0 == next_cost + && (*known_depth < next_depth || current.1 <= next_key)) + }) + }) { continue; } - if admitted.insert(next.clone()) && !budget.consume_node() { - truncated = true; - continue; + if !admitted.contains(&next) { + if !budget.consume_node() { + truncated = true; + continue; + } + admitted.insert(next.clone()); } - best.insert(next.clone(), candidate); - predecessor.insert(next.clone(), (node.clone(), edge.id.clone())); + // Every new state comes from an examined edge, so the shared + // edge budget also bounds the queue and predecessor storage. + best.entry(next.clone()) + .or_default() + .insert(next_depth, candidate); + predecessor.insert(next_state, (state.clone(), edge.id.clone())); queue.push(Reverse((next_cost, next_depth, next_key, next))); } } diff --git a/crates/compass-query/tests/code_traversal.rs b/crates/compass-query/tests/code_traversal.rs index da604fe73..02e38e611 100644 --- a/crates/compass-query/tests/code_traversal.rs +++ b/crates/compass-query/tests/code_traversal.rs @@ -3,13 +3,15 @@ mod support; use std::collections::HashSet; use std::fs; +use compass_graph::GraphSnapshotBuilder; use compass_model::code_graph::{EdgeKind, GraphDocument}; use compass_model::identity::{edge_id, file_id}; use compass_model::provenance::{OccurrenceRule, SourceAnchor}; use compass_model::query_contract::{ CallRequest, CodeQueryLimits, ImpactRequest, NodeTrailRequest, QueryDiagnosticCode, }; -use compass_query::open; +use compass_query::{open, open_with_store}; +use compass_store::SqliteStore; #[test] fn callers_include_calls_and_route_bindings_while_callees_follow_calls() @@ -465,6 +467,136 @@ fn node_trail_never_exceeds_node_or_edge_budgets() -> Result<(), Box Result<(), Box> { + support::write_graph(path)?; + let mut graph = GraphDocument::load(path)?; + let template = graph + .links + .iter() + .find(|edge| edge.kind == EdgeKind::Calls) + .cloned() + .ok_or("missing call template")?; + graph.nodes = ["n:s", "n:a", "n:b", "n:t"] + .into_iter() + .map(|id| support::node(id, compass_model::code_graph::NodeKind::Function, id, id)) + .collect(); + graph.links = edges + .iter() + .map(|(source, kind, target)| { + let mut edge = template.clone(); + edge.source = (*source).to_owned(); + edge.target = (*target).to_owned(); + edge.kind = *kind; + edge.occurrence_rule = None; + edge.id = edge_id(source, *kind, target, edge.relationship_site.as_ref(), None); + edge.key.clone_from(&edge.id); + edge + }) + .collect(); + fs::write(path, serde_json::to_vec(&graph)?)?; + Ok(()) +} + +#[test] +fn node_trail_keeps_a_costlier_shorter_prefix_that_can_reach_the_target() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = directory.path().join("graph.json"); + write_weighted_trail_fixture( + &graph_path, + &[ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:a", EdgeKind::Calls, "n:b"), + ("n:a", EdgeKind::Calls, "n:s"), + ("n:s", EdgeKind::References, "n:b"), + ("n:b", EdgeKind::Calls, "n:t"), + ], + )?; + for (max_depth, expected) in [ + (2, vec!["n:s", "n:b", "n:t"]), + (3, vec!["n:s", "n:a", "n:b", "n:t"]), + ] { + for reverse in [false, true] { + if reverse { + let mut graph = GraphDocument::load(&graph_path)?; + graph.nodes.reverse(); + graph.links.reverse(); + fs::write(&graph_path, serde_json::to_vec(&graph)?)?; + } + let graph = GraphDocument::load(&graph_path)?; + let store = SqliteStore::open( + directory + .path() + .join(format!("store-{max_depth}-{reverse}.db")), + )?; + let prepared = GraphSnapshotBuilder::new().prepare(&store, &graph)?; + GraphSnapshotBuilder::new().activate(&store, &prepared)?; + for engine in [ + open(&graph_path, None, &directory.path().join("cache"))?, + open_with_store( + &store, + &graph_path, + None, + &directory.path().join("store-cache"), + )?, + ] { + let response = engine.node_trail(NodeTrailRequest { + source: "n:s".to_owned(), + target: "n:t".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits { + max_depth, + max_edges: 5, + ..CodeQueryLimits::default() + }, + })?; + assert_eq!(response.paths.len(), 1); + assert_eq!(response.paths[0].node_ids, expected); + assert_eq!(response.paths[0].edge_ids.len(), expected.len() - 1); + } + } + } + Ok(()) +} + +#[test] +fn node_trail_does_not_readmit_a_rejected_node_without_paying_its_budget() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = directory.path().join("graph.json"); + write_weighted_trail_fixture( + &graph_path, + &[ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:s", EdgeKind::Calls, "n:b"), + ("n:a", EdgeKind::Calls, "n:b"), + ], + )?; + let engine = open(&graph_path, None, &directory.path().join("cache"))?; + let response = engine.node_trail(NodeTrailRequest { + source: "n:s".to_owned(), + target: "n:b".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits { + max_nodes: 2, + ..CodeQueryLimits::default() + }, + })?; + assert!(response.truncated); + assert!( + response.paths.is_empty(), + "a budget-rejected node was admitted on a second visit" + ); + assert!( + !response.nodes.iter().any(|node| node.id == "n:b"), + "a budget-rejected node leaked into the response on a second visit" + ); + Ok(()) +} + #[test] fn node_trail_excludes_graph_assembly_endpoint_remaps_by_default() -> Result<(), Box> { diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 28192484f..95801c900 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -421,6 +421,11 @@ compass explore "" ... [--format text|agent-json|json] compass node "" "" [--format text|agent-json|json] ``` +`node` searches directed, weighted trails within `--max-depth`. It retains +shorter and cheaper prefixes when either can affect reachability within that +hop limit. Node and edge work limits still apply: a truncated result is not +proof that no path exists. + `explore --format text` closes its bounded page with a `SOURCE` section: the recorded line range of each primary anchor, rendered from the digest-verified file the command already reads below `--root`. Blocks are bounded per anchor diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 735599027..7cbf2db3b 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -396,7 +396,8 @@ The report can include: - corpus and graph summary; - freshness/build metadata; -- god nodes; +- god nodes (connected source-located hub candidates, ordered by degree and + then stable node ID; a high rank is not proof of a god-object design defect); - communities; - surprising connections; - cycles/diagnostics; From 8a13928ef23a81e824fdf4374de06458623158a7 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:07:17 -0700 Subject: [PATCH 02/97] fix: ground code graph comparisons in fresh source evidence --- benchmarks/agent_query/README.md | 83 +++- benchmarks/agent_query/path_audit.py | 207 +++++++++ benchmarks/agent_query/path_witnesses.json | 209 +++++++++ benchmarks/agent_query/runner.py | 418 +++++++++++------- benchmarks/agent_query/suite_v2.toml | 4 +- .../agent_query/tests/test_path_audit.py | 127 ++++++ benchmarks/agent_query/tests/test_runner.py | 236 +++++++++- .../agent-query-evaluation-2026-09-23.md | 17 +- ...ode-graph-intelligence-audit-2026-09-26.md | 237 ++++++++++ 9 files changed, 1358 insertions(+), 180 deletions(-) create mode 100644 benchmarks/agent_query/path_audit.py create mode 100644 benchmarks/agent_query/path_witnesses.json create mode 100644 benchmarks/agent_query/tests/test_path_audit.py create mode 100644 docs/implementation/code-graph-intelligence-audit-2026-09-26.md diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 60663ae1d..71b16b1be 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -1,6 +1,6 @@ # Agent query evaluation -`benchmarks/agent-query` measures how well Compass answers the agent questions +`benchmarks/agent_query` measures how well Compass answers the agent questions in its suites compared with Graphify on the same pinned checkouts. It is developer-side tooling: Compass never runs it, and it never installs Graphify. @@ -43,22 +43,22 @@ Both suites contribute source-reviewed questions across `explain`, `brief_callers`, and `paged_callers` projections). Every question declares the exact per-tool argument vector, the expected outcome, and the file, line, or symbol anchors the reviewer read in the pinned checkout. Every repository also -declares graph anchors that both graphs must contain as source-backed nodes. +declares reviewed declarations to look for in each graph. ## Run ```bash -python3 benchmarks/agent-query/runner.py doctor \ +python3 benchmarks/agent_query/runner.py doctor \ --compass-binary /path/to/compass \ --graphify-binary /path/to/graphify \ --source cobra=/Volumes/Workspace/Github/spf13/cobra \ --source flask=/Volumes/Workspace/Github/pallets/flask \ --source gson=/Volumes/Workspace/Github/google/gson \ --source zod=/Volumes/Workspace/Github/colinhacks/zod \ - --source axum=/Volumes/Workspace/Github/tokio-rs/axum + --source axum=/Volumes/Workspace/Github/tokio-rs/axum/axum -python3 benchmarks/agent-query/runner.py run \ - --suite benchmarks/agent-query/suite_v2.toml \ +python3 benchmarks/agent_query/runner.py run \ + --suite benchmarks/agent_query/suite_v2.toml \ --workspace /Volumes/Workspace/CrabData/compass-evaluations/agent-query \ --compass-binary /Volumes/Workspace/crabbuild-target/compass/release/compass \ --graphify-binary "$(command -v graphify)" \ @@ -66,22 +66,37 @@ python3 benchmarks/agent-query/runner.py run \ --source flask=/Volumes/Workspace/Github/pallets/flask \ --source gson=/Volumes/Workspace/Github/google/gson \ --source zod=/Volumes/Workspace/Github/colinhacks/zod \ - --source axum=/Volumes/Workspace/Github/tokio-rs/axum + --source axum=/Volumes/Workspace/Github/tokio-rs/axum/axum ``` `--suite` defaults to `suite.toml` beside the runner. Passing `suite_v2.toml` runs the 50 blackbox questions instead. -`doctor` fails when a checkout is not at the suite's pinned commit. `run` +`doctor` fails unless the source is a clean working checkout at the suite's +pinned commit and contains the reviewed files. Axum's suite uses its `axum/` +package as the source root, not the monorepo root. `run` builds `compass extract --code-only --no-viz --store sqlite` and `graphify extract --code-only` once per repository under -`WORKSPACE/artifacts`, then writes `run.json` and `REPORT.md` under +`WORKSPACE/runs//artifacts`, then writes `run.json` and `REPORT.md` under `WORKSPACE/runs//`. +Every run builds fresh graphs and refuses an existing run ID. The retained +`--force` flag is a compatibility no-op. Prior artifact directories are never +silently reused or removed. The runner checks the pinned commit and clean Git +status before extraction and after queries, records graph digests and build +logs, and checks executable identity before and after the run. The executable +hash covers only that file: a Python launcher hash does not pin its imported +packages. Use an immutable environment when comparing installations. + ## Metrics -- **Correctness**: bounded stdout is judged against the suite's anchors. A - `negative` question passes only with an explicit no-match signal, and a +- **Correctness**: stdout with an accepted exit status and without timeout or + output-limit failure is judged against the suite's anchors. + Graphify `explain` deliberately returns exit 1 for ambiguity: that status is + accepted only for a pick-list question with its explicit ambiguity header and + at least two candidate IDs; the source-anchor oracle must still pass. Other + nonzero exits fail. A `negative` question passes only with an explicit + no-match signal, and a `pick_list` question passes only when the answer shows at least two distinct candidates and at least one reviewed candidate for the name; it deliberately does not require a specific pair, because a bounded page can only show part @@ -110,13 +125,55 @@ builds `compass extract --code-only --no-viz --store sqlite` and Graphify-only, and neither. - **Latency**: wall-clock milliseconds per tool invocation, including follow-ups. -- **Graph quality**: node and edge counts, source-backed node ratio, dangling - edges, duplicate IDs, and how many reviewed anchors the graph contains. +- **Graph coverage metadata**: node and edge counts, source-located node ratio, + dangling edges, duplicate IDs, and reviewed declaration anchors. Metadata + presence does not verify that the source or relationship is correct. + `compass.agent-query-run/2` uses `exact-file-start-terminal-symbol/1` on + both tools: exact repository-relative file, exact declaration start line, + and case-sensitive terminal symbol name. Qualification separators and + parameter lists are removed symmetrically; the pinned line distinguishes + overloads. This metric does not verify owner or parameter-type accuracy. + Enclosing module spans and unrelated names at the right line do not count. + Each graph metric records `anchorPolicy` and `missingAnchors` for review. + Historical v1 scores used file/line coverage without symbol identity and + must be recalculated before comparison with v2 scores. ## Limits +### Source-grounded path diagnostics + +The five positive `path` rows have a separate, stricter audit: + +```bash +python3 -m benchmarks.agent_query.path_audit \ + --run /path/to/workspace/runs/run-id \ + --output /path/to/new-path-audit.json +``` + +`path_witnesses.json` records reviewed declaration and occurrence lines in the +pinned source. The auditor checks the printed hop chain, unique node identity, +relation, direction, captured graph edge, and source occurrence. It refuses +ambiguous display labels instead of using the expected answer to select a node. +It verifies source state, suite and graph digests, and records response, witness, +and auditor hashes. The output must be new; earlier reports are retained. + +These witnesses were reviewed after observing output, so they are development +diagnostics, not held-out accuracy estimates. Gson accepts either an +instantiation at its construction line or a reference at its return-type line; +the report preserves the relation and reviewed site. Zod's file-containment +route proves navigation only. Neither is automatically credited as a call path. +The auditor currently requires successful, single-response executions of these +positive rows; it does not score negative or truncated path outcomes. + +### Interpretation + Anchor matching is a deterministic text-recall proxy over bounded output, not an independent precision oracle. The suite is a focused five-repository sample; it does not estimate population-wide accuracy. Graphify prints an installation warning on stderr, which `run.json` records separately and the token metric excludes. + +Each subprocess stream is capped at 16 MiB during capture. Exceeding either +cap terminates the process group and fails the observation; truncated text is +never scored as a successful response. An invalid Compass snapshot pointer +fails preparation instead of selecting an arbitrary unpublished snapshot. diff --git a/benchmarks/agent_query/path_audit.py b/benchmarks/agent_query/path_audit.py new file mode 100644 index 000000000..6f0b1e952 --- /dev/null +++ b/benchmarks/agent_query/path_audit.py @@ -0,0 +1,207 @@ +"""Audit captured path responses against graphs and reviewed source witnesses. + +This diagnoses the checked-in development cases. It does not estimate held-out +accuracy, accept endpoint echoes as paths, or choose among ambiguous labels. +Run with ``python3 -m benchmarks.agent_query.path_audit --help``. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re + +from benchmarks.agent_query.runner import _node_anchor, _sha256_file, _verify_source, load_suite + +MAX_GRAPH_BYTES = 256 * 1024 * 1024 +MAX_TEXT_BYTES = 16 * 1024 * 1024 +MAX_HOPS = 64 +HEADER = re.compile(r"(?:Best path \(weighted, (\d+) hops, weight [0-9.]+\)|Shortest path \((\d+) hops\)):") +EDGE = re.compile(r"--([a-z_]+) \[([A-Z_]+)\]-->|<--([a-z_]+) \[([A-Z_]+)\]--") +SAFE_KEY = re.compile(r"[a-z0-9][a-z0-9_-]*") + + +def read_bounded(path: Path, limit: int = MAX_TEXT_BYTES) -> bytes: + with path.open("rb") as stream: + data = stream.read(limit + 1) + if len(data) > limit: + raise ValueError(f"input exceeds {limit} bytes: {path}") + return data + + +def parse_path(text: str) -> tuple[list[str], list[dict]]: + lines = text.splitlines() + headers = [(index, match) for index, line in enumerate(lines) + if (match := HEADER.fullmatch(line))] + if len(headers) != 1: + raise ValueError("expected exactly one rendered path header") + index, header = headers[0] + hops = int(header[1] or header[2]) + if hops > MAX_HOPS or index + 1 >= len(lines): + raise ValueError("invalid path length or missing path body") + body = lines[index + 1].strip() + labels, steps, offset = [], [], 0 + for edge in EDGE.finditer(body): + labels.append(body[offset:edge.start()].strip()) + steps.append({"relation": edge[1] or edge[3], + "direction": "forward" if edge[1] else "reverse", + "confidence": edge[2] or edge[4]}) + offset = edge.end() + labels.append(body[offset:].strip()) + if len(steps) != hops or any(not label or len(label) > 512 for label in labels): + raise ValueError("printed hop count does not match the path body") + if any(line.strip() for line in lines[index + 2:]): + raise ValueError("unexpected text after rendered path") + return labels, steps + + +def check_source(root: Path, anchor: dict) -> None: + relative = Path(anchor["file"]) + path = (root / relative).resolve() + if relative.is_absolute() or ".." in relative.parts or not path.is_relative_to(root.resolve()): + raise ValueError("source witness escapes its root") + lines = read_bounded(path).decode("utf-8").splitlines() + line = anchor["line"] + if type(line) is not int or not 1 <= line <= len(lines): + raise ValueError("source witness line is outside the file") + if anchor["text"] not in lines[line - 1]: + raise ValueError(f"source witness changed at {relative}:{line}") + + +def audit_path(witness: dict, tool: str, graph: dict, output: str, source_root: Path) -> dict: + if tool not in ("compass", "graphify"): + raise ValueError("unsupported path audit tool") + for anchor in witness["nodes"]: + check_source(source_root, anchor) + for step in witness["steps"]: + if "site" in step: + check_source(source_root, step["site"]) + for site in step.get("sitesByRelation", {}).values(): + check_source(source_root, site) + result = {"tool": tool, "category": witness["category"], "matched": False, + "failures": [], "nodeIds": [], "steps": []} + try: + labels, steps = parse_path(output) + except ValueError as error: + result["failures"].append(str(error)) + return result + if len(labels) != len(witness["nodes"]) or len(steps) != len(witness["steps"]): + result["failures"].append("route length differs from reviewed witness") + return result + nodes = graph.get("nodes", []) + node_ids = [node.get("id") for node in nodes] + if any(not isinstance(identity, str) or not identity for identity in node_ids): + raise ValueError("graph node IDs must be nonempty strings") + if len(set(node_ids)) != len(node_ids): + raise ValueError("graph node IDs must be unique") + # Match exactly what the renderer prints. Never resolve a collision using + # the expected answer; doing so would conceal an ambiguous response. + for label, expected in zip(labels, witness["nodes"]): + candidates = [node for node in nodes + if node.get("name" if tool == "compass" else "label") == label] + if len(candidates) != 1: + result["failures"].append(f"unverified identity for {label!r}: {len(candidates)} matches") + return result + node = candidates[0] + file, line, _ = _node_anchor(node, tool) + if label not in expected["labels"] or (file, line) != (expected["file"], expected["line"]): + result["failures"].append(f"wrong source declaration for {label!r}") + return result + result["nodeIds"].append(node["id"]) + edges = graph.get("edges") or graph.get("links") or [] + for index, (actual, expected) in enumerate(zip(steps, witness["steps"])): + if actual["direction"] != expected["direction"] or actual["relation"] not in expected["relations"]: + result["failures"].append(f"hop {index + 1} has an unreviewed direction or relation") + continue + left, right = result["nodeIds"][index:index + 2] + source, target = (left, right) if actual["direction"] == "forward" else (right, left) + matches = [edge for edge in edges if ( + edge.get("_src", edge.get("source")), edge.get("_tgt", edge.get("target")), + edge.get("kind", edge.get("relation"))) == (source, target, actual["relation"])] + if not matches: + result["failures"].append(f"hop {index + 1} is absent from graph in printed direction") + site = expected.get("sitesByRelation", {}).get(actual["relation"], expected.get("site")) + grounded = None + if site is not None: + grounded = any(_edge_site(edge, tool) == (site["file"], site["line"]) for edge in matches) + if not grounded: + result["failures"].append(f"hop {index + 1} lacks the reviewed occurrence anchor") + result["steps"].append({**actual, "matchingEdges": len(matches), + "reviewedSite": site, "reviewedSiteSupported": grounded}) + result["matched"] = not result["failures"] + return result + + +def _edge_site(edge: dict, tool: str) -> tuple[object, object]: + if tool == "compass": + site = edge.get("relationshipSite") or {} + line = site.get("startLine") + return site.get("file"), line if type(line) is int and line > 0 else None + location = edge.get("source_location", "") + match = re.fullmatch(r"L([0-9]+)", location) if isinstance(location, str) else None + return edge.get("source_file"), int(match[1]) if match else None + + +def execute(args: argparse.Namespace) -> None: + root = args.run.resolve() + run = json.loads(read_bounded(root / "run.json")) + if run.get("schema") != "compass.agent-query-run/2": + raise ValueError("path audit requires a provenance-recorded v2 run") + if _sha256_file(root / "suite.toml") != run["suiteDigest"]: + raise ValueError("captured suite digest mismatch") + suite = load_suite(root / "suite.toml") + manifest_bytes = read_bounded(args.witnesses) + manifest = json.loads(manifest_bytes) + if manifest.get("schema") != "compass.agent-path-witnesses/1": + raise ValueError("unsupported path witness schema") + if not 1 <= len(manifest["witnesses"]) <= 1000: + raise ValueError("witness count outside audit limit") + repositories = {record["repository"]: record for record in run["repositories"]} + observations = {(row["repository"], row["question"], row["tool"]): row + for row in run["observations"]} + results = [] + for witness in manifest["witnesses"]: + repository, question = witness["repository"], witness["question"] + if not SAFE_KEY.fullmatch(repository) or not SAFE_KEY.fullmatch(question): + raise ValueError("unsafe witness identifier") + record = repositories[repository] + source = Path(record["source"]) + pinned = suite.repository(repository) + if witness["commit"] != pinned.commit or record["commit"] != pinned.commit: + raise ValueError("witness or captured source commit mismatch") + _verify_source(pinned, source) + for tool in ("compass", "graphify"): + graph_path = Path(record[f"{tool}Graph"]).resolve() + if not graph_path.is_relative_to(root): + raise ValueError("captured graph escapes the run directory") + graph_bytes = read_bounded(graph_path, MAX_GRAPH_BYTES) + if hashlib.sha256(graph_bytes).hexdigest() != record[f"{tool}GraphSha256"]: + raise ValueError("captured graph digest mismatch") + observation = observations[repository, question, tool] + if observation["exitCode"] != 0 or observation["timedOut"] or observation["followUps"]: + raise ValueError("path audit requires a successful single-response execution") + raw = read_bounded(root / "raw" / repository / f"{question}.{tool}.0.stdout") + if len(raw) != observation["stdoutBytes"]: + raise ValueError("captured response length mismatch") + result = audit_path(witness, tool, json.loads(graph_bytes), raw.decode("utf-8"), source) + results.append({"repository": repository, "question": question, + "stdoutSha256": hashlib.sha256(raw).hexdigest(), **result}) + _verify_source(pinned, source) + report = {"schema": "compass.agent-path-audit/1", "runId": run["runId"], + "scope": manifest["scope"], "witnessDigest": hashlib.sha256(manifest_bytes).hexdigest(), + "auditorDigest": _sha256_file(Path(__file__)), "results": results} + args.output.parent.mkdir(parents=True, exist_ok=True) + with args.output.open("x", encoding="utf-8") as stream: + stream.write(json.dumps(report, indent=2, sort_keys=True) + "\n") + for result in results: + print(result["repository"], result["tool"], result["matched"], result["failures"]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--run", type=Path, required=True) + parser.add_argument("--witnesses", type=Path, default=Path(__file__).with_name("path_witnesses.json")) + parser.add_argument("--output", type=Path, required=True) + execute(parser.parse_args()) diff --git a/benchmarks/agent_query/path_witnesses.json b/benchmarks/agent_query/path_witnesses.json new file mode 100644 index 000000000..1857b9eda --- /dev/null +++ b/benchmarks/agent_query/path_witnesses.json @@ -0,0 +1,209 @@ +{ + "schema": "compass.agent-path-witnesses/1", + "scope": "Development witnesses reviewed after observing output; not held-out accuracy estimates. Containment proves navigation, not execution flow. Gson accepts either source-supported instantiation or a return-type reference, recording their different relations and occurrences.", + "witnesses": [ + { + "repository": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "question": "cobra2-path-find-legacyargs", + "category": "call", + "nodes": [ + { + "file": "command.go", + "line": 757, + "text": "func (c *Command) Find(", + "labels": [ + ".Find()" + ] + }, + { + "file": "args.go", + "line": 28, + "text": "func legacyArgs(", + "labels": [ + "legacyArgs()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "command.go", + "line": 776, + "text": "legacyArgs(commandFound, stripFlags(a, commandFound))" + } + } + ] + }, + { + "repository": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "question": "flask2-path-fulldispatch-finalize", + "category": "call", + "nodes": [ + { + "file": "src/flask/app.py", + "line": 995, + "text": "def full_dispatch_request(", + "labels": [ + ".full_dispatch_request()" + ] + }, + { + "file": "src/flask/app.py", + "line": 1024, + "text": "def finalize_request(", + "labels": [ + ".finalize_request()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/flask/app.py", + "line": 1022, + "text": "self.finalize_request(ctx, rv)" + } + } + ] + }, + { + "repository": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "question": "gson2-path-newjsonwriter-jsonwriter", + "category": "construction-reference", + "nodes": [ + { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(", + "labels": [ + ".newJsonWriter()" + ] + }, + { + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "text": "public class JsonWriter", + "labels": [ + "JsonWriter" + ] + } + ], + "steps": [ + { + "relations": [ + "instantiates", + "references" + ], + "direction": "forward", + "sitesByRelation": { + "instantiates": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 801, + "text": "new JsonWriter(writer)" + }, + "references": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(" + } + } + } + ] + }, + { + "repository": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "question": "zod2-path-detectversion-convertschema", + "category": "file-containment", + "nodes": [ + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "text": "function detectVersion(", + "labels": [ + "detectVersion()" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 1, + "text": "", + "labels": [ + "from-json-schema", + "from-json-schema.ts" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 808, + "text": "function convertSchema(", + "labels": [ + "convertSchema()" + ] + } + ], + "steps": [ + { + "relations": [ + "contains" + ], + "direction": "reverse" + }, + { + "relations": [ + "contains" + ], + "direction": "forward" + } + ] + }, + { + "repository": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "question": "axum2-path-validate-v07", + "category": "call", + "nodes": [ + { + "file": "src/routing/path_router.rs", + "line": 22, + "text": "fn validate_path(", + "labels": [ + "validate_path()" + ] + }, + { + "file": "src/routing/path_router.rs", + "line": 36, + "text": "fn validate_v07_paths(", + "labels": [ + "validate_v07_paths()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/routing/path_router.rs", + "line": 30, + "text": "validate_v07_paths(path)?" + } + } + ] + } + ] +} diff --git a/benchmarks/agent_query/runner.py b/benchmarks/agent_query/runner.py index 2b3ffcbfe..23f6069df 100644 --- a/benchmarks/agent_query/runner.py +++ b/benchmarks/agent_query/runner.py @@ -25,15 +25,16 @@ import os from pathlib import Path import re -import shutil import signal import subprocess import sys import time +import threading import tomllib SUITE_SCHEMA = "compass.agent-query-suite/1" -RUN_SCHEMA = "compass.agent-query-run/1" +RUN_SCHEMA = "compass.agent-query-run/2" +GRAPH_ANCHOR_POLICY = "exact-file-start-terminal-symbol/1" TOKEN_BYTES = 4 MAX_OUTPUT_BYTES = 16 * 1024 * 1024 DEFAULT_TIMEOUT_SECONDS = 60.0 @@ -314,6 +315,7 @@ class CommandResult: stderr_bytes: int stdout: str stderr: str + output_limited: bool = False def run_bounded( @@ -328,32 +330,52 @@ def run_bounded( stdout_path.parent.mkdir(parents=True, exist_ok=True) started = time.monotonic() timed_out = False - with stdout_path.open("wb") as stdout_stream, stderr_path.open("wb") as stderr_stream: - process = subprocess.Popen( - argv, - cwd=cwd, - stdout=stdout_stream, - stderr=stderr_stream, - start_new_session=True, - ) + exceeded = threading.Event() + capture_errors: list[Exception] = [] + process = subprocess.Popen( + argv, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + start_new_session=True, + ) + + def capture(pipe, path: Path) -> None: try: - process.wait(timeout=timeout_seconds) - except subprocess.TimeoutExpired: - timed_out = True + with pipe, path.open("wb") as stream: + remaining = MAX_OUTPUT_BYTES + while chunk := pipe.read1(64 * 1024): + stream.write(chunk[:remaining]) + if len(chunk) > remaining: + exceeded.set() + return + remaining -= len(chunk) + except Exception as error: + capture_errors.append(error) + exceeded.set() + + readers = [ + threading.Thread(target=capture, args=(process.stdout, stdout_path), daemon=True), + threading.Thread(target=capture, args=(process.stderr, stderr_path), daemon=True), + ] + for reader in readers: + reader.start() + while process.poll() is None or any(reader.is_alive() for reader in readers): + if exceeded.is_set() or time.monotonic() - started >= timeout_seconds: + timed_out = not exceeded.is_set() try: - os.killpg(process.pid, signal.SIGTERM) - process.wait(timeout=5) - except (ProcessLookupError, subprocess.TimeoutExpired): - try: - os.killpg(process.pid, subprocess.signal.SIGKILL) - except ProcessLookupError: - pass - process.wait() + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + break + time.sleep(0.01) + process.wait(timeout=5) + for reader in readers: + reader.join(timeout=5) + if capture_errors or any(reader.is_alive() for reader in readers): + raise RuntimeError(f"incomplete subprocess output capture: {capture_errors}") wall_ms = int((time.monotonic() - started) * 1000) stdout_bytes = stdout_path.stat().st_size stderr_bytes = stderr_path.stat().st_size - stdout = stdout_path.read_bytes()[:MAX_OUTPUT_BYTES].decode("utf-8", errors="replace") - stderr = stderr_path.read_bytes()[:MAX_OUTPUT_BYTES].decode("utf-8", errors="replace") + stdout = stdout_path.read_bytes().decode("utf-8", errors="replace") + stderr = stderr_path.read_bytes().decode("utf-8", errors="replace") return CommandResult( argv=argv, exit_code=process.returncode, @@ -363,6 +385,7 @@ def run_bounded( stderr_bytes=stderr_bytes, stdout=stdout, stderr=stderr, + output_limited=exceeded.is_set(), ) @@ -483,6 +506,22 @@ def _tool_argv( return tuple(base) +def _accepted_exit(question: Question, tool: str, result: CommandResult) -> bool: + if result.exit_code == 0: + return True + # Graphify 0.9.67's explain command explicitly exits 1 for an ambiguity + # list. This is a valid pick-list outcome, not an execution failure. The + # independent candidate/anchor oracle must still pass before crediting it. + return ( + result.exit_code == 1 + and tool == "graphify" + and question.expect == "pick_list" + and question.graphify[:1] == ("explain",) + and result.stdout.startswith("Ambiguous:") + and _candidate_count(tool, result.stdout) >= 2 + ) + + def run_question( repository: Repository, question: Question, @@ -527,6 +566,15 @@ def run_question( pages.append(result.stdout) aggregate = "\n".join(pages) attempt_pass, attempt_failures = judge(question, tool, aggregate) + if result.output_limited: + attempt_pass = False + attempt_failures += ("command output limit exceeded",) + elif result.timed_out: + attempt_pass = False + attempt_failures += ("command timed out",) + elif not _accepted_exit(question, tool, result): + attempt_pass = False + attempt_failures += (f"command exited {result.exit_code}",) if attempt == 0: first_page_tokens = tokens first_page_pass = attempt_pass @@ -537,7 +585,7 @@ def run_question( failures = attempt_failures if attempt == question.max_follow_ups: break - if result.timed_out or result.exit_code != 0: + if result.output_limited or result.timed_out or result.exit_code != 0: break if tool == "compass": match = _CURSOR.search(result.stdout) @@ -579,10 +627,7 @@ def _compass_graph(artifact_root: Path) -> Path: graph = output / "snapshots" / snapshot / "graph.json" if graph.is_file(): return graph - candidates = sorted(output.glob("snapshots/*/graph.json")) - if not candidates: - raise RuntimeError(f"no Compass graph under {output}") - return candidates[-1] + raise RuntimeError(f"invalid or missing published Compass snapshot under {output}") def _graphify_graph(artifact_root: Path) -> Path: @@ -599,62 +644,60 @@ def prepare_repository( *, compass_binary: Path, graphify_binary: Path, - force: bool, timeout_seconds: float, ) -> dict: root = artifacts / repository.name compass_root = root / "compass" graphify_root = root / "graphify" record: dict[str, object] = {"repository": repository.name, "source": str(source)} - if force: - for path in (compass_root, graphify_root): - if path.exists(): - shutil.rmtree(path) - if not (compass_root / "compass-out").is_dir(): - started = time.monotonic() - result = run_bounded( - ( - str(compass_binary), - "extract", - str(source), - "--code-only", - "--no-viz", - "--store", - "sqlite", - "--out", - str(compass_root), - ), - cwd=source, - timeout_seconds=timeout_seconds, - stdout_path=root / "compass-build.stdout", - stderr_path=root / "compass-build.stderr", - ) - record["compassBuildMs"] = int((time.monotonic() - started) * 1000) - record["compassBuildExit"] = result.exit_code - record["compassBuildReport"] = result.stdout.strip().splitlines()[-3:] - if result.exit_code != 0: - raise RuntimeError(f"Compass extract failed for {repository.name}: {result.stderr[-2000:]}") - if not (graphify_root / "graphify-out" / "graph.json").is_file(): - started = time.monotonic() - result = run_bounded( - ( - str(graphify_binary), - "extract", - str(source), - "--code-only", - "--out", - str(graphify_root), - ), - cwd=source, - timeout_seconds=timeout_seconds, - stdout_path=root / "graphify-build.stdout", - stderr_path=root / "graphify-build.stderr", - ) - record["graphifyBuildMs"] = int((time.monotonic() - started) * 1000) - record["graphifyBuildExit"] = result.exit_code - record["graphifyBuildReport"] = result.stdout.strip().splitlines()[-3:] - if result.exit_code != 0: - raise RuntimeError(f"Graphify extract failed for {repository.name}: {result.stderr[-2000:]}") + # Every run owns fresh artifacts. Never infer build provenance from a path. + root.mkdir(parents=True, exist_ok=False) + record["commit"] = repository.commit + started = time.monotonic() + result = run_bounded( + ( + str(compass_binary), + "extract", + str(source), + "--code-only", + "--no-viz", + "--store", + "sqlite", + "--out", + str(compass_root), + ), + cwd=source, + timeout_seconds=timeout_seconds, + stdout_path=root / "compass-build.stdout", + stderr_path=root / "compass-build.stderr", + ) + record["compassBuildMs"] = int((time.monotonic() - started) * 1000) + record["compassBuildExit"] = result.exit_code + record["compassBuildArgv"] = list(result.argv) + record["compassBuildReport"] = result.stdout.strip().splitlines()[-3:] + if result.output_limited or result.timed_out or result.exit_code != 0: + raise RuntimeError(f"Compass extract failed for {repository.name}: {result.stderr[-2000:]}") + started = time.monotonic() + result = run_bounded( + ( + str(graphify_binary), + "extract", + str(source), + "--code-only", + "--out", + str(graphify_root), + ), + cwd=source, + timeout_seconds=timeout_seconds, + stdout_path=root / "graphify-build.stdout", + stderr_path=root / "graphify-build.stderr", + ) + record["graphifyBuildMs"] = int((time.monotonic() - started) * 1000) + record["graphifyBuildExit"] = result.exit_code + record["graphifyBuildArgv"] = list(result.argv) + record["graphifyBuildReport"] = result.stdout.strip().splitlines()[-3:] + if result.output_limited or result.timed_out or result.exit_code != 0: + raise RuntimeError(f"Graphify extract failed for {repository.name}: {result.stderr[-2000:]}") compass_graph = _compass_graph(compass_root) graphify_graph = _graphify_graph(graphify_root) record["compassGraph"] = str(compass_graph) @@ -664,35 +707,47 @@ def prepare_repository( return record +def _terminal_symbol(value: object) -> str: + """Compare declaration names, not substrings or enclosing source spans. + + A pinned file and exact declaration start distinguish overloads. Both tools + may omit qualification or parameter text, so those are not scored as proof + of owner identity or signature accuracy. + """ + if not isinstance(value, str): + return "" + name = value.split("(", 1)[0].strip() + return name.replace("::", ".").rsplit(".", 1)[-1] + + +def _node_anchor(node: dict, tool: str) -> tuple[str | None, int | None, set[str]]: + if tool == "compass": + source = node.get("source") + source = source if isinstance(source, dict) else {} + file = source.get("file") + line = source.get("startLine") + names = (node.get("name"), node.get("qualifiedName"), node.get("label")) + elif tool == "graphify": + file = node.get("source_file") + location = node.get("source_location") + match = re.fullmatch(r"L([0-9]+)", location) if isinstance(location, str) else None + line = int(match[1]) if match else None + names = (node.get("label"), node.get("name"), node.get("qualifiedName")) + else: + raise ValueError(f"unsupported graph metric tool {tool!r}") + # bool is an int subclass in Python; it is never a valid source line. + line = line if type(line) is int and line > 0 else None + file = file if isinstance(file, str) and file else None + return file, line, {name for value in names if (name := _terminal_symbol(value))} + + def graph_metrics(repository: Repository, tool: str, graph: Path) -> dict: document = json.loads(graph.read_text(encoding="utf-8")) nodes = document.get("nodes", []) edges = document.get("edges") or document.get("links") or [] - if tool == "compass": - identifiers = [node.get("id") for node in nodes] - sourced = sum(1 for node in nodes if (node.get("source") or {}).get("file")) - locations = [ - ( - (node.get("source") or {}).get("file"), - (node.get("source") or {}).get("startLine"), - (node.get("source") or {}).get("endLine"), - ) - for node in nodes - ] - else: - identifiers = [node.get("id") for node in nodes] - sourced = sum(1 for node in nodes if node.get("source_file")) - locations = [] - for node in nodes: - file = node.get("source_file") - location = node.get("source_location") - line = None - if isinstance(location, str) and location.startswith("L"): - try: - line = int(location[1:]) - except ValueError: - line = None - locations.append((file, line, line)) + identifiers = [node.get("id") for node in nodes] + locations = [_node_anchor(node, tool) for node in nodes] + sourced = sum(1 for file, _, _ in locations if file) known = {identifier for identifier in identifiers if isinstance(identifier, str)} dangling = 0 for edge in edges: @@ -702,17 +757,21 @@ def graph_metrics(repository: Repository, tool: str, graph: Path) -> dict: dangling += 1 duplicates = len(identifiers) - len(known) anchor_hits = 0 + missing_anchors = [] for anchor in repository.anchors: - for file, start, end in locations: - if file != anchor.file or start is None: - continue - stop = end if isinstance(end, int) else start - if isinstance(start, int) and start <= anchor.line <= stop: - anchor_hits += 1 - break + symbol = _terminal_symbol(anchor.symbol) + matched = bool(symbol) and any( + file == anchor.file and line == anchor.line and symbol in names + for file, line, names in locations + ) + if matched: + anchor_hits += 1 + else: + missing_anchors.append({"file": anchor.file, "line": anchor.line, "symbol": anchor.symbol}) return { "tool": tool, "graph": str(graph), + "anchorPolicy": GRAPH_ANCHOR_POLICY, "nodes": len(nodes), "edges": len(edges), "sourceBackedNodes": sourced, @@ -721,6 +780,7 @@ def graph_metrics(repository: Repository, tool: str, graph: Path) -> dict: "duplicateIds": duplicates, "anchorHits": anchor_hits, "anchorTotal": len(repository.anchors), + "missingAnchors": missing_anchors, } @@ -753,9 +813,18 @@ def render_report(run: dict) -> str: lines.append("document for their text budgets; `answer tokens` sums every response the") lines.append("reviewed workflow needed, including documented follow-up pages.") lines.append("") - lines.append("## Graph quality") + lines.append("## Graph coverage metadata") + lines.append("") + policies = sorted({m.get("anchorPolicy", "legacy-file-line-coverage") for m in run["graphMetrics"]}) + lines.append("Graph anchor policies: " + ", ".join(f"`{policy}`" for policy in policies) + ".") + if policies == [GRAPH_ANCHOR_POLICY]: + lines.append("A reviewed graph anchor requires the exact file, declaration start line,") + lines.append("and terminal symbol name on both tools. Enclosing file/module spans do not count.") + else: + lines.append("Legacy graph-anchor scores did not check symbol names and are not comparable to the new policy.") + lines.append("Source-located counts describe metadata presence, not verified source correctness.") lines.append("") - lines.append("| Repository | Language | Tool | Nodes | Edges | Source-backed | Dangling | Duplicate IDs | Reviewed anchors |") + lines.append("| Repository | Language | Tool | Nodes | Edges | Source-located | Dangling | Duplicate IDs | Reviewed anchors |") lines.append("| --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |") for metrics in run["graphMetrics"]: lines.append( @@ -773,7 +842,7 @@ def render_report(run: dict) -> str: ) ) lines.append("") - lines.append("## Query results") + lines.append("## Query text-oracle results") lines.append("") lines.append("| Repository | Question | Kind | Compass | C tokens | G tokens | Compass ms | Graphify ms |") lines.append("| --- | --- | --- | --- | ---: | ---: | ---: | ---: |") @@ -835,7 +904,7 @@ def render_report(run: dict) -> str: lines.append(f"- Token efficiency (median answer tokens): {verdict['tokens']}") lines.append(f"- Token efficiency (paired answers only): {verdict['pairedTokens']}") lines.append(f"- Oracle split: {verdict['split']}") - lines.append(f"- Graph quality (source-backed ratio and reviewed anchors): {verdict['graphQuality']}") + lines.append(f"- Graph coverage metadata (source locations and reviewed anchors): {verdict['graphQuality']}") lines.append("") lines.append("## Limits") lines.append("") @@ -896,28 +965,17 @@ def _aggregate(run: dict) -> None: f"Graphify leads ({graphify['passed']}/{graphify['questions']} vs " f"{compass['passed']}/{compass['questions']})" ) - if graphify["medianAnswerTokens"] and compass["medianAnswerTokens"]: - ratio = graphify["medianAnswerTokens"] / compass["medianAnswerTokens"] - if compass["medianAnswerTokens"] <= graphify["medianAnswerTokens"]: - verdict["tokens"] = ( - f"Compass is cheaper per answered question " - f"({compass['medianAnswerTokens']:.0f} vs {graphify['medianAnswerTokens']:.0f} tokens, " - f"{ratio:.2f}x)" - ) - else: - verdict["tokens"] = ( - f"Graphify is cheaper per answered question " - f"({graphify['medianAnswerTokens']:.0f} vs {compass['medianAnswerTokens']:.0f} tokens, " - f"{1 / ratio:.2f}x)" - ) - else: - verdict["tokens"] = "Not comparable: one tool has no passing answers" + verdict["tokens"] = ( + f"Unpaired medians: Compass {compass['medianAnswerTokens']:.0f}, " + f"Graphify {graphify['medianAnswerTokens']:.0f} estimated tokens; " + "these can cover different passing questions and do not establish a cost advantage" + ) both = paired["all"] if both["bothPassed"]: verdict["pairedTokens"] = ( - f"On the {both['bothPassed']} questions both tools answered, Compass costs " + f"On the {both['bothPassed']} questions both tools passed, Compass uses " f"{both['medianCompassTokens']:.0f} tokens and Graphify " - f"{both['medianGraphifyTokens']:.0f} tokens (median)" + f"{both['medianGraphifyTokens']:.0f} estimated tokens (median)" ) else: verdict["pairedTokens"] = "No question was answered by both tools" @@ -935,7 +993,7 @@ def _aggregate(run: dict) -> None: compass_ratio = compass_sourced / compass_nodes if compass_nodes else 0.0 graphify_ratio = graphify_sourced / graphify_nodes if graphify_nodes else 0.0 verdict["graphQuality"] = ( - f"Compass {compass_ratio:.1%} source-backed and {compass_anchors} reviewed anchors vs " + f"Compass {compass_ratio:.1%} source-located and {compass_anchors} reviewed anchors vs " f"Graphify {graphify_ratio:.1%} and {graphify_anchors}" ) run["verdict"] = verdict @@ -1017,7 +1075,9 @@ def _tool_identity(name: str, binary: Path) -> dict: return { "name": name, "binary": str(binary), + # For a script this identifies the launcher, not its imported packages. "binarySha256": _sha256_file(binary), + "digestScope": "executable-file-only", "version": version_text, } @@ -1028,10 +1088,39 @@ def _parse_sources(values: list[str] | None) -> dict[str, Path]: name, separator, path = value.partition("=") if not separator or not name or not path: raise SystemExit(f"--source must be NAME=PATH, got {value!r}") - sources[name] = Path(path) + sources[name] = Path(path).resolve() return sources +def _verify_source(repository: Repository, source: Path) -> None: + bare = subprocess.run( + ("git", "rev-parse", "--is-bare-repository"), cwd=source, check=True, + stdout=subprocess.PIPE, text=True, timeout=30, + ).stdout.strip() + if bare != "false": + raise RuntimeError(f"{repository.name}: source must be a working checkout, not a bare repository") + head = subprocess.run( + ("git", "rev-parse", "HEAD"), cwd=source, check=True, + stdout=subprocess.PIPE, text=True, timeout=30, + ).stdout.strip() + if head != repository.commit: + raise RuntimeError( + f"{repository.name}: checkout HEAD {head} does not match pinned {repository.commit}" + ) + status = subprocess.run( + ("git", "status", "--porcelain", "--untracked-files=normal"), + cwd=source, check=True, stdout=subprocess.PIPE, text=True, timeout=30, + ).stdout + if status: + raise RuntimeError(f"{repository.name}: evaluation requires a clean pinned checkout") + for anchor in repository.anchors: + if not (source / anchor.file).is_file(): + raise RuntimeError( + f"{repository.name}: source root lacks reviewed source file {anchor.file}; " + "choose the suite's package root" + ) + + def execute(args: argparse.Namespace) -> int: suite = load_suite(args.suite) repositories = [ @@ -1042,12 +1131,28 @@ def execute(args: argparse.Namespace) -> int: if not repositories: raise SystemExit("no repositories selected") sources = _parse_sources(args.source) + for repository in repositories: + if repository.name not in sources and args.corpus_root is not None: + sources[repository.name] = (args.corpus_root / repository.name).resolve() + source = sources.get(repository.name) + if source is None or not source.is_dir(): + raise SystemExit(f"{repository.name}: pass --source {repository.name}=PATH or --corpus-root") + _verify_source(repository, source) workspace = args.workspace.resolve() - artifacts = workspace / "artifacts" stamp = args.run_id or datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") + if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", stamp): + raise SystemExit("--run-id must be a single safe directory name") run_root = workspace / "runs" / stamp + artifacts = run_root / "artifacts" raw_root = run_root / "raw" - run_root.mkdir(parents=True, exist_ok=True) + run_root.mkdir(parents=True, exist_ok=False) + runner_digest = _sha256_file(Path(__file__)) + (run_root / "runner.py").write_bytes(Path(__file__).read_bytes()) + (run_root / "suite.toml").write_bytes(suite.path.read_bytes()) + tools = [ + _tool_identity("compass", args.compass_binary), + _tool_identity("graphify", args.graphify_binary), + ] started = datetime.now(timezone.utc).isoformat() prepared: list[dict] = [] metrics: list[dict] = [] @@ -1058,24 +1163,13 @@ def execute(args: argparse.Namespace) -> int: source = args.corpus_root / repository.name if source is None or not source.is_dir(): raise SystemExit(f"{repository.name}: pass --source {repository.name}=PATH or --corpus-root") - head = subprocess.run( - ("git", "rev-parse", "HEAD"), - cwd=source, - check=True, - stdout=subprocess.PIPE, - text=True, - ).stdout.strip() - if head != repository.commit: - raise SystemExit( - f"{repository.name}: checkout HEAD {head} does not match pinned {repository.commit}" - ) + _verify_source(repository, source) record = prepare_repository( repository, source, artifacts, compass_binary=args.compass_binary, graphify_binary=args.graphify_binary, - force=args.force, timeout_seconds=args.build_timeout, ) prepared.append(record) @@ -1109,16 +1203,23 @@ def execute(args: argparse.Namespace) -> int: + (f" {list(observation.failures)}" if not observation.passed else ""), flush=True, ) + _verify_source(repository, source) + current_tools = [ + _tool_identity("compass", args.compass_binary), + _tool_identity("graphify", args.graphify_binary), + ] + if _sha256_file(Path(__file__)) != runner_digest: + raise RuntimeError("runner changed during evaluation; refusing to publish scores") + if current_tools != tools: + raise RuntimeError("tool identity changed during evaluation; refusing to publish scores") run = { "schema": RUN_SCHEMA, "runId": stamp, "startedAt": started, "completedAt": datetime.now(timezone.utc).isoformat(), "suiteDigest": suite.digest, - "tools": [ - _tool_identity("compass", args.compass_binary), - _tool_identity("graphify", args.graphify_binary), - ], + "runnerDigest": runner_digest, + "tools": tools, "repositories": prepared, "graphMetrics": metrics, "questions": [ @@ -1158,12 +1259,11 @@ def doctor(args: argparse.Namespace) -> int: print(f"{repository.name}: missing source (pass --source {repository.name}=PATH)") failures += 1 continue - head = subprocess.run( - ("git", "rev-parse", "HEAD"), cwd=source, check=True, stdout=subprocess.PIPE, text=True - ).stdout.strip() - status = "pinned" if head == repository.commit else f"HEAD {head} != {repository.commit}" - print(f"{repository.name}: {source} ({status})") - if head != repository.commit: + try: + _verify_source(repository, source) + print(f"{repository.name}: {source} (clean and pinned)") + except (RuntimeError, subprocess.SubprocessError) as error: + print(f"{repository.name}: {error}") failures += 1 return 1 if failures else 0 @@ -1202,7 +1302,7 @@ def build_parser() -> argparse.ArgumentParser: run_parser.add_argument("--workspace", type=Path, required=True) run_parser.add_argument("--run-id") run_parser.add_argument("--repository", action="append") - run_parser.add_argument("--force", action="store_true", help="rebuild both graphs") + run_parser.add_argument("--force", action="store_true", help="deprecated compatibility flag; every run builds fresh graphs") run_parser.add_argument("--build-timeout", type=float, default=1800.0) run_parser.add_argument("--query-timeout", type=float, default=DEFAULT_TIMEOUT_SECONDS) run_parser.set_defaults(handler=execute) diff --git a/benchmarks/agent_query/suite_v2.toml b/benchmarks/agent_query/suite_v2.toml index ed80c8f9e..f0e5be48a 100644 --- a/benchmarks/agent_query/suite_v2.toml +++ b/benchmarks/agent_query/suite_v2.toml @@ -328,9 +328,9 @@ judgment = "Gson.java:565 declares public String toJson(Object src)." [[repository.anchor]] file = "gson/src/main/java/com/google/gson/stream/JsonWriter.java" -line = 527 +line = 526 symbol = "value(String)" -judgment = "JsonWriter.java:527 declares public JsonWriter value(String value)." +judgment = "JsonWriter.java:526 starts the @CanIgnoreReturnValue annotation on public JsonWriter value(String value), whose method header is line 527." [[repository.anchor]] file = "gson/src/main/java/com/google/gson/TypeAdapter.java" diff --git a/benchmarks/agent_query/tests/test_path_audit.py b/benchmarks/agent_query/tests/test_path_audit.py new file mode 100644 index 000000000..48e6a2eed --- /dev/null +++ b/benchmarks/agent_query/tests/test_path_audit.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import copy +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.path_audit import audit_path, parse_path + + +class PathAuditTests(unittest.TestCase): + def setUp(self) -> None: + self.temporary = tempfile.TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + (self.root / "code.rs").write_text("fn start() {}\nfn finish() {}\nfinish();\n") + self.witness = { + "category": "call", + "nodes": [ + {"file": "code.rs", "line": 1, "text": "fn start()", "labels": ["start()"]}, + {"file": "code.rs", "line": 2, "text": "fn finish()", "labels": ["finish()"]}, + ], + "steps": [{"relations": ["calls"], "direction": "forward", + "site": {"file": "code.rs", "line": 3, "text": "finish();"}}], + } + self.compass = { + "nodes": [ + {"id": "s", "name": "start()", "source": {"file": "code.rs", "startLine": 1}}, + {"id": "t", "name": "finish()", "source": {"file": "code.rs", "startLine": 2}}, + ], + "edges": [{"source": "s", "target": "t", "kind": "calls", + "relationshipSite": {"file": "code.rs", "startLine": 3}}], + } + self.graphify = { + "nodes": [ + {"id": "s", "label": "start()", "source_file": "code.rs", "source_location": "L1"}, + {"id": "t", "label": "finish()", "source_file": "code.rs", "source_location": "L2"}, + ], + "links": [{"source": "t", "target": "s", "_src": "s", "_tgt": "t", + "relation": "calls", "source_file": "code.rs", "source_location": "L3"}], + } + self.output = "Shortest path (1 hops):\n start() --calls [EXTRACTED]--> finish()\n" + + def audit(self, graph=None, output=None, tool="compass"): + return audit_path(self.witness, tool, graph or self.compass, + self.output if output is None else output, self.root) + + def test_both_formats_and_semantic_direction_are_supported(self) -> None: + self.assertTrue(self.audit()["matched"]) + result = self.audit(self.graphify, tool="graphify") + self.assertTrue(result["matched"]) + self.assertTrue(result["steps"][0]["reviewedSiteSupported"]) + compass = self.output.replace("Shortest path (1 hops)", "Best path (weighted, 1 hops, weight 1)") + self.assertTrue(self.audit(output=compass)["matched"]) + + def test_endpoint_mentions_and_false_headers_do_not_prove_a_path(self) -> None: + for text in ("start() finish()", "No path found between start() and finish()", + self.output.replace("1 hops", "2 hops"), self.output + self.output): + with self.subTest(text=text): + self.assertFalse(self.audit(output=text)["matched"]) + + def test_wrong_printed_direction_and_relation_fail(self) -> None: + for text in (self.output.replace("--calls [EXTRACTED]-->", "<--calls [EXTRACTED]--"), + self.output.replace("--calls", "--references")): + self.assertFalse(self.audit(output=text)["matched"]) + + def test_missing_or_reversed_graph_edge_fails(self) -> None: + graph = copy.deepcopy(self.compass) + graph["edges"] = [] + self.assertFalse(self.audit(graph)["matched"]) + graph["edges"] = [{"source": "t", "target": "s", "kind": "calls"}] + self.assertFalse(self.audit(graph)["matched"]) + + def test_wrong_occurrence_and_wrong_declaration_fail(self) -> None: + for field in ("site", "declaration"): + graph = copy.deepcopy(self.compass) + if field == "site": + graph["edges"][0]["relationshipSite"]["startLine"] = 2 + else: + graph["nodes"][0]["source"]["startLine"] = 2 + self.assertFalse(self.audit(graph)["matched"]) + + def test_ambiguous_labels_are_not_resolved_using_the_expected_answer(self) -> None: + graph = copy.deepcopy(self.compass) + rival = copy.deepcopy(graph["nodes"][0]) + rival["id"] = "other" + rival["source"]["file"] = "elsewhere.rs" + graph["nodes"].append(rival) + result = self.audit(graph) + self.assertFalse(result["matched"]) + self.assertIn("unverified identity", result["failures"][0]) + + def test_source_witness_drift_fails_instead_of_scoring(self) -> None: + (self.root / "code.rs").write_text("changed\n") + with self.assertRaisesRegex(ValueError, "source witness changed"): + self.audit() + + def test_coarse_relation_uses_its_own_reviewed_source_site(self) -> None: + step = self.witness["steps"][0] + step["relations"].append("references") + step["sitesByRelation"] = { + "calls": step.pop("site"), + "references": {"file": "code.rs", "line": 1, "text": "fn start()"}, + } + graph = copy.deepcopy(self.compass) + graph["edges"][0]["kind"] = "references" + output = self.output.replace("--calls", "--references") + self.assertFalse(self.audit(graph, output)["matched"]) + graph["edges"][0]["relationshipSite"]["startLine"] = 1 + self.assertTrue(self.audit(graph, output)["matched"]) + graph["edges"][0]["relationshipSite"]["startLine"] = True + self.assertFalse(self.audit(graph, output)["matched"]) + + def test_invalid_or_duplicate_ids_fail_instead_of_scoring(self) -> None: + for identity in (None, "", 1, "s"): + graph = copy.deepcopy(self.compass) + graph["nodes"][1]["id"] = identity + with self.subTest(identity=identity), self.assertRaisesRegex(ValueError, "graph node IDs"): + self.audit(graph) + + def test_hop_count_is_bounded(self) -> None: + with self.assertRaises(ValueError): + parse_path("Shortest path (1000000 hops):\n start()\n") + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmarks/agent_query/tests/test_runner.py b/benchmarks/agent_query/tests/test_runner.py index 44bd726e9..e82d700d2 100644 --- a/benchmarks/agent_query/tests/test_runner.py +++ b/benchmarks/agent_query/tests/test_runner.py @@ -3,10 +3,19 @@ import json from pathlib import Path import tempfile +import sys +import subprocess import unittest +from unittest.mock import patch from benchmarks.agent_query.runner import ( Anchor, + CommandResult, + _compass_graph, + _verify_source, + prepare_repository, + run_question, + run_bounded, Question, Repository, _paired_summary, @@ -166,6 +175,170 @@ def test_paired_tokens_are_zero_without_a_shared_answer(self) -> None: self.assertEqual(summary["medianGraphifyTokens"], 0) +class ExecutionEvidenceTests(unittest.TestCase): + def test_source_root_must_contain_the_reviewed_anchor_files(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + with tempfile.TemporaryDirectory() as temporary: + responses = [subprocess.CompletedProcess([], 0, stdout=value) for value in ( + "false\n", repository.commit + "\n", "", + )] + with patch("benchmarks.agent_query.runner.subprocess.run", side_effect=responses): + with self.assertRaisesRegex(RuntimeError, "reviewed source file"): + _verify_source(repository, Path(temporary)) + + def test_bare_repository_is_not_accepted_as_source(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + with tempfile.TemporaryDirectory() as temporary: + source = Path(temporary) + subprocess.run(("git", "init", "--bare", "--quiet", str(source)), check=True) + with self.assertRaisesRegex(RuntimeError, "working checkout"): + _verify_source(repository, source) + + def test_capture_limits_both_streams_without_unbounded_disk_files(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + for stream in ("stdout", "stderr"): + with self.subTest(stream=stream): + with patch("benchmarks.agent_query.runner.MAX_OUTPUT_BYTES", 4096): + result = run_bounded( + (sys.executable, "-c", f"import sys; sys.{stream}.write('x' * 100000)"), + cwd=root, timeout_seconds=5, + stdout_path=root / "stdout", stderr_path=root / "stderr", + ) + self.assertTrue(result.output_limited) + self.assertLessEqual((root / "stdout").stat().st_size, 4096) + self.assertLessEqual((root / "stderr").stat().st_size, 4096) + + def test_capture_preserves_success_and_reports_timeout(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + result = run_bounded( + (sys.executable, "-c", "import sys; print('answer'); print('notice', file=sys.stderr)"), + cwd=root, timeout_seconds=5, + stdout_path=root / "stdout", stderr_path=root / "stderr", + ) + self.assertEqual(result.stdout, "answer\n") + self.assertEqual(result.stderr, "notice\n") + self.assertEqual(result.exit_code, 0) + self.assertFalse(result.timed_out or result.output_limited) + result = run_bounded( + (sys.executable, "-c", "import time; time.sleep(10)"), + cwd=root, timeout_seconds=0.05, + stdout_path=root / "stdout", stderr_path=root / "stderr", + ) + self.assertTrue(result.timed_out) + self.assertFalse(result.output_limited) + self.assertNotEqual(result.exit_code, 0) + + def test_graphify_ambiguity_exit_one_is_a_valid_pick_list_only(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + output = "Ambiguous: 'sample' matches 2 nodes in different files.\n sample.go\n id: a\n other.go\n id: b\n" + oracle = question( + kind="ambiguity", expect="pick_list", required_one_of=("sample.go",), + min_one_of=1, min_candidates=2, + ) + result = CommandResult((), 1, False, 1, len(output), 0, output, "") + with patch("benchmarks.agent_query.runner.run_bounded", return_value=result): + observation = run_question( + repository, oracle, tool="graphify", binary=Path("tool"), + graph=Path("graph.json"), cwd=ROOT, raw_dir=ROOT, timeout_seconds=1, + ) + self.assertTrue(observation.passed) + self.assertEqual(observation.exit_code, 1) + + def test_graphify_exit_one_exception_does_not_accept_other_outcomes(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + output = "Ambiguous: 'sample' matches 2 nodes in different files.\n sample.go\n id: a\n other.go\n id: b\n" + base = dict(kind="ambiguity", expect="pick_list", required_one_of=("sample.go",), min_one_of=1, min_candidates=2) + cases = ( + (question(**base), output.replace("Ambiguous:", "Error:")), + (question(**base), output.replace(" id: b", "")), + (question(**base, graphify=("query", "sample")), output), + (question(), output), + (question(**{**base, "required_one_of": ("missing.go",)}), output), + ) + for oracle, stdout in cases: + with self.subTest(oracle=oracle, stdout=stdout): + result = CommandResult((), 1, False, 1, len(stdout), 0, stdout, "") + with patch("benchmarks.agent_query.runner.run_bounded", return_value=result): + observation = run_question( + repository, oracle, tool="graphify", binary=Path("tool"), + graph=Path("graph.json"), cwd=ROOT, raw_dir=ROOT, timeout_seconds=1, + ) + self.assertFalse(observation.passed) + + def test_failed_or_timed_out_output_cannot_pass(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + for tool in ("compass", "graphify"): + for exit_code, timed_out, output_limited in ((1, False, False), (0, True, False), (0, False, True)): + with self.subTest(tool=tool, exit_code=exit_code, timed_out=timed_out): + result = CommandResult((), exit_code, timed_out, 1, 9, 0, "sample.go", "", output_limited) + with patch("benchmarks.agent_query.runner.run_bounded", return_value=result): + observation = run_question( + repository, question(), tool=tool, binary=Path("tool"), + graph=Path("graph.json"), cwd=ROOT, raw_dir=ROOT, + timeout_seconds=1, + ) + self.assertFalse(observation.passed) + self.assertFalse(observation.first_page_pass) + self.assertTrue(observation.failures) + + def test_snapshot_pointer_cannot_fall_back_to_an_unpublished_graph(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + snapshot = root / "compass-out" / "snapshots" / "snapshot-other" + snapshot.mkdir(parents=True) + (snapshot / "graph.json").write_text("{}") + (root / "compass-out" / "current-snapshot").write_text("snapshot-missing") + with self.assertRaisesRegex(RuntimeError, "published Compass snapshot"): + _compass_graph(root) + + def test_each_new_run_builds_both_graphs_and_retains_build_arguments(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + def build(argv, **kwargs): + output = Path(argv[-1]) + if argv[0] == "compass": + snapshot = output / "compass-out" / "snapshots" / "snapshot-current" + snapshot.mkdir(parents=True) + (snapshot / "graph.json").write_text("{}") + (output / "compass-out" / "current-snapshot").write_text("snapshot-current") + else: + (output / "graphify-out").mkdir(parents=True) + (output / "graphify-out" / "graph.json").write_text("{}") + return CommandResult(argv, 0, False, 1, 0, 0, "", "") + with tempfile.TemporaryDirectory() as temporary: + with patch("benchmarks.agent_query.runner.run_bounded", side_effect=build) as run: + for name in ("first", "second"): + record = prepare_repository( + repository, ROOT, Path(temporary) / name, + compass_binary=Path("compass"), graphify_binary=Path("graphify"), + timeout_seconds=1, + ) + self.assertEqual(record["commit"], repository.commit) + for tool in ("compass", "graphify"): + self.assertEqual(record[f"{tool}BuildArgv"][:2], [tool, "extract"]) + self.assertEqual(len(record[f"{tool}GraphSha256"]), 64) + self.assertEqual(run.call_count, 4) + + def test_existing_artifacts_are_never_silently_reused_or_removed(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + with tempfile.TemporaryDirectory() as temporary: + artifacts = Path(temporary) + root = artifacts / repository.name + root.mkdir() + marker = root / "prior-graph.json" + marker.write_text("prior evidence") + with patch("benchmarks.agent_query.runner.run_bounded") as run: + with self.assertRaises(FileExistsError): + prepare_repository( + repository, ROOT, artifacts, + compass_binary=Path("compass"), graphify_binary=Path("graphify"), + timeout_seconds=1, + ) + run.assert_not_called() + self.assertEqual(marker.read_text(), "prior evidence") + + class EstimateTests(unittest.TestCase): def test_tokens_round_up_by_four_bytes(self) -> None: self.assertEqual(estimate_tokens(0), 0) @@ -278,6 +451,61 @@ def test_pick_list_counts_graphify_ambiguity_candidates(self) -> None: class GraphMetricTests(unittest.TestCase): + def metric_for_node(self, tool: str, node: dict, symbol: str = "parse") -> dict: + repository = Repository( + name="sample", + language="TypeScript", + url="https://example.invalid/sample.git", + commit="0" * 40, + questions=(), + anchors=(Anchor(file="sample.ts", line=42, symbol=symbol, judgment="reviewed"),), + ) + with tempfile.TemporaryDirectory() as directory: + graph = Path(directory) / "graph.json" + graph.write_text(json.dumps({"nodes": [node], "links": []}), encoding="utf-8") + return graph_metrics(repository, tool, graph) + + def test_enclosing_module_does_not_prove_a_declaration_anchor(self) -> None: + metrics = self.metric_for_node("compass", { + "id": "module", + "name": "sample", + "kind": "module", + "source": {"file": "sample.ts", "startLine": 1, "endLine": 100}, + }) + self.assertEqual(metrics["anchorHits"], 0) + + def test_anchor_requires_symbol_identity_for_both_tools(self) -> None: + for tool in ("compass", "graphify"): + for name in ("safeParse", "parseOther", "Parse", "", "unrelated"): + with self.subTest(tool=tool, name=name): + node = {"id": "wrong", "name": name, "label": name} + if tool == "compass": + node["source"] = {"file": "sample.ts", "startLine": 42, "endLine": 50} + else: + node.update(source_file="sample.ts", source_location="L42") + self.assertEqual(self.metric_for_node(tool, node)["anchorHits"], 0) + + def test_anchor_uses_exact_declaration_start_not_an_overlapping_span(self) -> None: + for start in (1, 41, 43, True): + with self.subTest(start=start): + metrics = self.metric_for_node("compass", { + "id": "wrong-overload", + "name": "parse", + "source": {"file": "sample.ts", "startLine": start, "endLine": 90}, + }) + self.assertEqual(metrics["anchorHits"], 0) + + def test_qualified_names_and_signature_labels_use_the_same_rule(self) -> None: + for tool in ("compass", "graphify"): + for name in ("parse", "Schema.parse", "Schema::parse", "parse(Input)"): + with self.subTest(tool=tool, name=name): + node = {"id": "declaration", "name": name, "label": name} + if tool == "compass": + node["source"] = {"file": "sample.ts", "startLine": 42, "endLine": 50} + else: + node.update(source_file="sample.ts", source_location="L42") + self.assertEqual(self.metric_for_node(tool, node, "Schema.parse(Input)")["anchorHits"], 1) + def test_compass_metrics_find_dangling_and_duplicate_records(self) -> None: repository = Repository( name="sample", @@ -294,10 +522,12 @@ def test_compass_metrics_find_dangling_and_duplicate_records(self) -> None: "nodes": [ { "id": "one", - "source": {"file": "a.go", "startLine": 1, "endLine": 5}, + "name": "A", + "source": {"file": "a.go", "startLine": 3, "endLine": 5}, }, { "id": "one", + "name": "B", "source": {"file": "b.go", "startLine": 9, "endLine": 12}, }, ], @@ -324,8 +554,8 @@ def test_graphify_metrics_match_declaration_lines(self) -> None: ) document = { "nodes": [ - {"id": "one", "source_file": "a.go", "source_location": "L42"}, - {"id": "two", "source_file": "a.go", "source_location": "L7"}, + {"id": "one", "label": "A", "source_file": "a.go", "source_location": "L42"}, + {"id": "two", "label": "A", "source_file": "a.go", "source_location": "L7"}, ], "links": [], } diff --git a/docs/implementation/agent-query-evaluation-2026-09-23.md b/docs/implementation/agent-query-evaluation-2026-09-23.md index ee0ad125f..0b5bb4715 100644 --- a/docs/implementation/agent-query-evaluation-2026-09-23.md +++ b/docs/implementation/agent-query-evaluation-2026-09-23.md @@ -1,10 +1,21 @@ # Agent query evaluation: five repositories, five languages +> Audit correction, 2026-09-26: the original `compass.agent-query-run/1` +> graph-anchor scorer ignored the requested symbol and credited any Compass +> node whose source span covered the requested line. A module could therefore +> stand in for a missing declaration. The anchor counts below must not be used +> as evidence of declaration coverage until replayed with the corrected v2 +> scorer. The source-backed ratios also measure metadata presence, not source +> correctness. Historical query pass counts are text-oracle results on this +> suite, not a current-release or population-wide accuracy claim. See the +> [continuing audit](code-graph-intelligence-audit-2026-09-26.md). + ## Result -Compass answers the reviewed agent questions more accurately and publishes a -better-anchored graph than Graphify on this suite, but it still spends more -tokens per answered question. The evaluation ran on 2026-09-23 against Compass +The historical text oracle credited more Compass responses on this suite, +with higher token estimates per passing response. Its graph-anchor comparison +is invalidated by the scorer defect described above; these results do not +establish better graph correctness. The evaluation ran on 2026-09-23 against Compass commit `3fd246dc` plus the fixes in this change, and Graphify `0.9.36`. | Metric | Compass | Graphify | diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md new file mode 100644 index 000000000..5ca0d8936 --- /dev/null +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -0,0 +1,237 @@ +# Code graph intelligence audit: 2026-09-26 + +## Status and acceptance criteria + +Broad superiority over Graphify is **unproven**. The objective covers hub +analysis, code graph correctness, queries, explanations, and navigation/path +finding. A focused text-recall score cannot establish all of those properties. + +| Requirement | Evidence needed | Current evidence | +| --- | --- | --- | +| Reliable hub analysis | Declaration-aware candidates, stable rankings, source-reviewed false positives and negatives | Four hub defects fixed; two bounded-trail defects fixed; no reviewed god-object corpus yet | +| Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Anchor scorer repaired; relationship accuracy not measured by that scorer | +| Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Existing five-repository suites are development samples with text oracles | +| Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Prior query changes exist; fresh paired evidence still needed | +| Better navigation and walks | Valid ordered edges, direction, hop bounds, alternatives, ambiguity and negative cases | Existing path tests/suites are useful but do not prove real-repository path precision | +| Fair efficiency comparison | Same successful questions, repeated timings, token methodology and complete environment provenance | Paired token aggregation exists; bytes/4 remains an estimate | + +“God mode” is interpreted here as the existing `god_nodes` hub analysis. +It orders connected candidates by degree; it does not measure responsibility, +cohesion, or whether a high-degree declaration needs refactoring. + +## Reproduced production defects + +`crates/compass-graph/src/analyze.rs` previously discarded names such as +`Path` and `Counter` even when they were project declarations with source +locations. It also used graph input order to break degree ties, and returned +isolated declarations when enough results were requested. + +The fix retains those source-located declarations, breaks ties by stable node +ID, and omits degree-zero candidates. Two regression tests failed before the +fix; all ten tests in `analyze_coverage` passed afterward. An MCP regression +checks the rendered ordering and top-N behavior. Public fields and degree +semantics are unchanged; candidate lists can change. + +A fourth defect treated every `.method()` label as a file even when the node +had canonical kind `method`, and allowed explicitly typed files with descriptive +labels into the hub list. The regression returned `[caller, file]` where the +source-located candidates were `[method, caller, function]`. Recognized canonical +kinds now take precedence; the label heuristic remains for legacy unknown kinds. +The updated graph-analysis integration suite passes all eleven tests. + +Remaining limitation: legacy file/concept/JSON-noise eligibility still uses heuristics. +Degree combines relationship kinds and counts directed endpoint pairs, not +responsibilities or call-site occurrences. A popular infrastructure type may +be a legitimate hub. A separate design diagnosis requires reviewed evidence +and an explicit metric contract before it can be claimed. + +## Reproduced navigation defects + +Two adversarial native regressions exposed problems in the typed `node` search: + +1. Keeping only the cheapest arrival at each node loses feasible paths under a + hop limit. For `s -> a -> b -> t` (cheap calls) and `s -> b` (costlier + reference), a two-hop request incorrectly returned no path. The search now + retains nondominated cost/depth states and reconstructs the exact state path. +2. A node rejected by the node budget was inserted into the admitted set before + the budget check. A second visit could admit it without paying, leaking that + rejected node into a truncated response. Admission now happens only after + successful budget consumption. + +Both tests failed before the changes and passed afterward. The full traversal +integration suite passes 11/11, including JSON and SQLite checks, record-order +permutations, two/three-hop expectations and a cycle under a five-edge work +budget. Positive-cost cycles are dominated rather than repeatedly expanded. +All search labels and predecessor records remain bounded by examined edges. +The CLI contract regression also passed, within the 34-test CLI query suite. + +## Evaluation corrections + +The v1 graph-anchor scorer ignored the requested symbol. For Compass, any +node span covering a reviewed line could earn credit, including a whole +module. For Graphify, any name at that line could earn credit. Fourteen +negative subcases reproduced false positives in the old scorer. + +Run schema v2 records `exact-file-start-terminal-symbol/1`: exact file, +exact declaration start, and case-sensitive terminal name on both tools. +Qualification/signature text is stripped symmetrically; this does not verify +owner identity or parameter types. Missing anchors are listed for review. +Source-located ratios are metadata counts, not verified source correctness. + +The runner also previously reused graph files by directory existence and +reported the current executable identity. A changed tool could be credited +with an old graph. Runs now build fresh artifacts beneath their own run +directory and refuse existing run IDs/artifact directories. They check clean +pinned Git state before extraction and after querying, retain graph digests +and build logs, and compare executable identities before/after the run. +Executable-file hashes do not cover Python imports or the full environment. + +A response containing the requested strings could pass even if its process +failed or timed out. Such executions now fail independently of text matching. +Capture now enforces a 16 MiB per-stream disk cap during execution; limit +failures cannot pass. An invalid snapshot pointer cannot silently select an +unpublished graph. Unpaired token medians no longer produce a cost-winner claim. +The existing text oracle remains a recall proxy: mentioning both endpoints +does not prove a valid path, and mentioning a caller does not prove its edge. + +The historical report is annotated and its unsupported graph-quality conclusion +withdrawn. Its original raw artifact directories are unavailable on this host, +so the historical graph scores have not been recalculated. Do not substitute +newer tool outputs for that missing historical evidence. + +## Available comparison inputs + +Cobra, Flask, Gson and Zod have clean working checkouts at the suite commits. +The old `doctor` accepted Axum's bare Git repository merely because HEAD matched; +that directory has no source tree to extract. The runner and doctor now reject +bare repositories. A separate Axum working checkout was created and verified at the suite commit. +Existing checkouts remain read-only. +The installed Compass reports 0.3.29 and cannot represent this working branch. +Installed Graphify reports 0.9.67; the old report used 0.9.36. The separately +available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c48` +(version 0.9.63), so its implementation is not assumed identical to 0.9.67. + +## Verification ledger + +- Graph analysis integration suite: 11/11 passed after all four production fixes. +- Benchmark Python unit suite: 38/38 passed, including ten path-auditor tests. +- CLI query contract suite: 34/34 passed; product suite: 9/9 passed. +- Query relevance qualification: 5/5 passed, including the 500 synthetic cases. +- Workspace Clippy (`--workspace --lib --bins --locked -- -D warnings`): passed + after the explicit-kind and bounded-trail corrections. +- Rust formatting check: passed. +- Product boundary script: passed; competitor tooling stays outside production. +- Workspace native tests (`--workspace --lib --bins --locked`): 1,081 passed, + zero failed, two ignored after all production fixes, including both MCP + regressions. +- Code-graph fixture qualification: initial native stages passed; the React + oracle then failed because locked TypeScript dependencies were absent. + After `npm ci --ignore-scripts`, the complete final gate passed (exit 0), + including deterministic production updates, semantic/topology assertions, + Markdown quality, and independent React source-anchor checks. +- First v2 replay: complete but invalidated for comparative scoring (see below). +- Corrected v2 replay `v2-corrected-02`: complete, after all three evaluation + corrections. It uses the debug binary and recorded source patch from before + the explicit-kind hub and typed-trail fixes, with Graphify 0.9.67. + +## Findings from the first fresh replay + +The raw evidence is retained under the `code-graph-audit-20260926` evaluation +workspace, run `v2-fresh-01`, with a separate invalidation record. Before scoring +it as a comparison, three issues required correction (now implemented): + +- Graphify 0.9.67 deliberately exits 1 when `explain` returns an ambiguity list. + Its installed `cli.py` and the captured Cobra, Flask and Gson responses confirm + this. The new blanket nonzero-exit rejection wrongly penalizes a correct + pick-list outcome. The scorer now permits this explicit contract only when the + candidate oracle passes; actual command failures and timeouts still fail. +- Gson's `JsonWriter.value(String)` annotation starts at line 526; its method + header is line 527. Both tools correctly locate the declaration at 526. + The reviewed anchor now uses the annotation start. These were not extraction + failures; the matching policy remains exact. +- Axum's suite uses paths relative to the `axum/` package, while the separate + checkout is the monorepo root. The corrected replay uses that package as the + source root, and preflight now rejects roots missing reviewed files. + +The source patch and Graphify distribution file hashes were retained alongside +this run. Timings from the debug Compass executable are not release-performance +evidence. Preliminary score totals must not be presented as accuracy results. + +## Corrected focused comparison + +The corrected replay uses the exact source roots and reviewed declaration starts +and accepts Graphify's documented ambiguity exit status. Both executable +identities remained unchanged and all five source roots remained clean/pinned. +Its suite and runner copies, tool hashes, graphs, logs and raw responses are +retained under `runs/v2-corrected-02` in the evaluation workspace. + +| Measured item | Compass | Graphify 0.9.67 | +| --- | ---: | ---: | +| Text-oracle passes, all rows | 50/50 | 44/50 | +| Non-excerpt rows | 45/45 | 44/45 | +| Source-excerpt rows | 5/5 | 0/5 | +| Exact reviewed declaration anchors | 15/15 | 14/15 | +| Median estimated tokens on 44 shared passes | 308 | 111.5 | + +The largest difference is a source-excerpt feature gap: Graphify's `explain` +returns metadata rather than the requested declaration text. Those five rows +are separated above instead of treating them as five independent relationship +accuracy wins. Among the other 45 rows, the difference is one Axum file-path +lookup. Both path inputs resolve to the same unrelated test module. A follow-up +using the full `src/routing/...` paths produced the same failure; the raw retry +is retained as `axum-exact-path.stdout`/`.stderr`. This is evidence of that file +lookup failure, not proof that Graphify cannot traverse an explicit-ID path. +Its missing Zod anchor is the implementation at `classic/schemas.ts:303`; its +graph retains the interface declaration at line 72 instead. + +Graphify has the lower paired token estimate. These are bytes/4 estimates, not +measured model tokens. No speed comparison is claimed: Compass used a debug +binary, other native checks ran concurrently, and timings were single samples. +All 50 questions are an existing development suite. The six extra passes do +not establish held-out precision, execution-path accuracy or overall superiority. +The subsequent native trail defects demonstrate gaps this text suite misses. +The separate 500-query relevance qualification runs AI-reviewed synthetic +phrasing-equivalence cases over the shared fixture graph, as its test and +corpus notes explicitly state. It is useful regression coverage, not 500 +independent real-repository judgments or production telemetry. + +## Source-grounded path review + +The five positive path responses from `v2-corrected-02` were audited separately +against the printed ordered hops, unique graph node identities, semantic edge +direction, relation, declaration anchors, and reviewed source occurrences. +Both tools pass all five navigation witnesses in `path-audit-02.json`. +This is post-output development review, not held-out precision or recall. + +The first diagnostic incorrectly required Gson's construction line 801 even +when allowing the coarse `references` relation. Graphify's actual edge is a +valid return-type reference at line 797. The corrected witness accepts that +site for `references` and line 801 for `instantiates`; Compass returns the +latter. The report preserves this specificity difference. The earlier +`path-audit-01.json` remains as an oracle-error diagnostic, not a competitor +failure. Zod's route uses reverse/forward file containment in both tools; +it supports navigation but proves no runtime call chain. + +The checked-in auditor rejects missing/reversed edges, wrong occurrences, +wrong declarations, ambiguous labels, invalid identities, endpoint-only text, +and inconsistent hop counts. It checks recorded graph/suite digests and pinned +source state, bounds inputs, and records hashes for its own code, witnesses, +and captured responses. It currently audits successful positive path rows; +unreachable, ambiguous, truncated and limit outcomes still need a broader +source-reviewed corpus. + +## Next evidence to collect + +1. Finish native checks and build the current branch executable on the workspace + volume. Record the binary digest, source revision and local patch state. +2. Replay both tools in a fresh run under the corrected policy. Review missing + declaration anchors against pinned source before attributing extraction gaps. +3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, + traversal direction, source occurrences, ambiguity, unreachable nodes, and + bound exhaustion. A negative or limit outcome must never count as a path. +4. Use held-out repositories/questions and publish all failures, including + competitor wins. Separate extraction gaps, resolution gaps, retrieval gaps, + rendering gaps and oracle mistakes using actual source evidence. +5. Improve the owning production layer for reproduced failures, retain native + regressions, then rerun equivalent questions. Report category-level evidence + and uncertainty rather than claiming universal dominance. From c30cf4ab12185e8e55a4cc28f1532aec1325ae2b Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:13:55 -0700 Subject: [PATCH 03/97] fix: retain structural hubs from extensionless sources --- CHANGELOG.md | 3 +- COMPATIBILITY.md | 2 ++ crates/compass-graph/src/analyze.rs | 7 +++- .../compass-graph/tests/analyze_coverage.rs | 29 +++++++++++++++ crates/compass-mcp/src/lib.rs | 26 ++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 36 ++++++++++++++----- 6 files changed, 93 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d981d2613..0cd88a361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ without charging the traversal budget. - Respect explicit graph node kinds in topology analysis: method-shaped labels - remain callable candidates, and explicitly typed files stay out of hub lists. + and extensionless source files retain their structural candidates, and + explicitly typed files stay out of hub lists. - Make god-node ranking stable for equal-degree nodes, retain project declarations whose names overlap library names, and omit isolated nodes. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 68a34f035..ad6e32c1d 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -111,6 +111,8 @@ of input record order. Source-located declarations named `Path`, `Counter`, alone. Explicit canonical node kinds take precedence over display-label heuristics throughout topology analysis: a `.method()` label does not turn a method into a file, and file nodes remain excluded even with descriptive labels. +Typed structural nodes with nonempty source paths are not classified as concepts +merely because their source filename has no extension. Legacy records without a recognized kind retain the existing label fallback. Isolated declarations are omitted. The serialized `id`, `label`, and `degree` fields and degree calculation are unchanged; the candidate list can diff --git a/crates/compass-graph/src/analyze.rs b/crates/compass-graph/src/analyze.rs index 0db078d7d..9fb7ed3d0 100644 --- a/crates/compass-graph/src/analyze.rs +++ b/crates/compass-graph/src/analyze.rs @@ -1804,7 +1804,12 @@ fn explicit_node_kind(node: &NodeRecord) -> Option { fn is_concept_node(node: &NodeRecord) -> bool { let source = attribute(node, "source_file").unwrap_or_default(); - source.is_empty() || !source.rsplit('/').next().unwrap_or_default().contains('.') + // Canonical structural records can come from extensionless scripts or + // configuration files. Retain the filename heuristic for legacy records + // without a recognized kind, and still exclude nodes lacking a source. + source.is_empty() + || (explicit_node_kind(node).is_none() + && !source.rsplit('/').next().unwrap_or_default().contains('.')) } fn is_json_key_node(node: &NodeRecord) -> bool { attribute(node, "source_file").is_some_and(|source| source.to_lowercase().ends_with(".json")) diff --git a/crates/compass-graph/tests/analyze_coverage.rs b/crates/compass-graph/tests/analyze_coverage.rs index 93010f148..97bd3e8e6 100644 --- a/crates/compass-graph/tests/analyze_coverage.rs +++ b/crates/compass-graph/tests/analyze_coverage.rs @@ -102,6 +102,35 @@ fn god_nodes_use_explicit_kinds_instead_of_method_label_heuristics() { assert_eq!(ranked[0].degree, 3); } +#[test] +fn god_nodes_preserve_typed_declarations_in_extensionless_sources() { + let graph = document( + vec![ + json!({"id":"prepare", "kind":"function", "name":"prepare()", + "source":{"file":"bin/launch", "startLine":2}}), + json!({"id":"helper", "kind":"function", "name":"helper()", + "source":{"file":"src/support.sh", "startLine":1}}), + json!({"id":"external", "kind":"function", "name":"external()"}), + json!({"id":"concept", "label":"Idea", "source_file":"conversation"}), + ], + vec![ + edge("prepare", "helper", "calls", "EXTRACTED"), + edge("prepare", "external", "calls", "EXTRACTED"), + edge("prepare", "concept", "references", "EXTRACTED"), + ], + true, + ); + let ranked = god_nodes(&graph, 10); + assert_eq!( + ranked + .iter() + .map(|node| node.id.as_str()) + .collect::>(), + ["prepare", "helper"] + ); + assert_eq!(ranked[0].degree, 3); +} + #[test] fn god_nodes_do_not_label_isolated_declarations_as_hubs() { let graph = document( diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 62dd5eb61..6d656090b 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -3241,6 +3241,32 @@ mod tests { Ok(()) } + #[test] + fn god_nodes_tool_retains_extensionless_typed_source() -> Result<(), Box> + { + let temp = tempfile::tempdir()?; + let graph = temp.path().join("graph.json"); + fs::write( + &graph, + serde_json::to_vec(&json!({ + "directed": true, + "nodes": [ + {"id":"prepare", "kind":"function", "name":"prepare()", + "source":{"file":"bin/launch", "startLine":2}}, + {"id":"helper", "kind":"function", "name":"helper()", + "source":{"file":"src/support.sh", "startLine":1}} + ], + "links": [{"source":"prepare", "target":"helper", "relation":"calls"}] + }))?, + )?; + let server = CompassMcp::new(&graph); + assert_eq!( + server.invoke("god_nodes", Map::new()), + "God nodes (most connected):\n 1. helper() - 1 edges\n 2. prepare() - 1 edges" + ); + Ok(()) + } + #[test] fn every_local_tool_and_resource_handles_success_missing_and_filter_shapes() -> Result<(), Box> { diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 5ca0d8936..9207fd1e9 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -8,7 +8,7 @@ finding. A focused text-recall score cannot establish all of those properties. | Requirement | Evidence needed | Current evidence | | --- | --- | --- | -| Reliable hub analysis | Declaration-aware candidates, stable rankings, source-reviewed false positives and negatives | Four hub defects fixed; two bounded-trail defects fixed; no reviewed god-object corpus yet | +| Reliable hub analysis | Declaration-aware candidates, stable rankings, source-reviewed false positives and negatives | Five hub defects fixed; no reviewed god-object corpus yet | | Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Anchor scorer repaired; relationship accuracy not measured by that scorer | | Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Existing five-repository suites are development samples with text oracles | | Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Prior query changes exist; fresh paired evidence still needed | @@ -39,6 +39,14 @@ source-located candidates were `[method, caller, function]`. Recognized canonica kinds now take precedence; the label heuristic remains for legacy unknown kinds. The updated graph-analysis integration suite passes all eleven tests. +A fifth defect excluded typed functions whose source filename has no extension. +An MCP diagnostic using the release binary omitted `prepare()` in `bin/launch`, +but included the identical node when its source was `bin/launch.sh`. A native +regression reproduced the failure. Canonical structural kinds now take +precedence over the concept filename heuristic when the source path is nonempty. +Nodes without a source remain excluded, and unknown legacy kinds retain the +existing fallback. The graph-analysis integration suite passes all twelve tests. + Remaining limitation: legacy file/concept/JSON-noise eligibility still uses heuristics. Degree combines relationship kinds and counts directed endpoint pairs, not responsibilities or call-site occurrences. A popular infrastructure type may @@ -113,7 +121,7 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c ## Verification ledger -- Graph analysis integration suite: 11/11 passed after all four production fixes. +- Graph analysis integration suite: 12/12 passed after all five hub fixes. - Benchmark Python unit suite: 38/38 passed, including ten path-auditor tests. - CLI query contract suite: 34/34 passed; product suite: 9/9 passed. - Query relevance qualification: 5/5 passed, including the 500 synthetic cases. @@ -122,8 +130,9 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c - Rust formatting check: passed. - Product boundary script: passed; competitor tooling stays outside production. - Workspace native tests (`--workspace --lib --bins --locked`): 1,081 passed, - zero failed, two ignored after all production fixes, including both MCP - regressions. + zero failed, two ignored at the six-defect checkpoint, including both MCP + regressions. Workspace tests/Clippy are being rerun after the extensionless + source correction and its additional MCP regression. - Code-graph fixture qualification: initial native stages passed; the React oracle then failed because locked TypeScript dependencies were absent. After `npm ci --ignore-scripts`, the complete final gate passed (exit 0), @@ -133,6 +142,12 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c - Corrected v2 replay `v2-corrected-02`: complete, after all three evaluation corrections. It uses the debug binary and recorded source patch from before the explicit-kind hub and typed-trail fixes, with Graphify 0.9.67. +- Release replay `v2-release-03`: complete at source commit `8a13928e`, including + the six original production fixes, before the extensionless-source correction. + Scores and paired token estimates match the corrected debug replay. Its + source-grounded path audit passes 5/5 per tool. All 92 previously recorded + Graphify source/data file hashes remain unchanged after the run; this does not + pin every transitive dependency. ## Findings from the first fresh replay @@ -187,6 +202,11 @@ graph retains the interface declaration at line 72 instead. Graphify has the lower paired token estimate. These are bytes/4 estimates, not measured model tokens. No speed comparison is claimed: Compass used a debug binary, other native checks ran concurrently, and timings were single samples. +The later release checkpoint reproduced every score and paired token median +in the table. Its single-run paired median latency was 259.5 ms for Compass and +216 ms for Graphify; this also does not establish a repeatable speed advantage. +The release executable and build log were retained with a source-commit record +and SHA-256, and both tool identities were unchanged across the replay. All 50 questions are an existing development suite. The six extra passes do not establish held-out precision, execution-path accuracy or overall superiority. The subsequent native trail defects demonstrate gaps this text suite misses. @@ -222,10 +242,10 @@ source-reviewed corpus. ## Next evidence to collect -1. Finish native checks and build the current branch executable on the workspace - volume. Record the binary digest, source revision and local patch state. -2. Replay both tools in a fresh run under the corrected policy. Review missing - declaration anchors against pinned source before attributing extraction gaps. +1. Finish the native rerun for the extensionless-source correction; keep the + production build and exact source provenance with each comparison checkpoint. +2. Expand hub review beyond candidate eligibility to source-reviewed design + judgments, separating connectivity from responsibility/cohesion defects. 3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, traversal direction, source occurrences, ambiguity, unreachable nodes, and bound exhaustion. A negative or limit outcome must never count as a path. From c9478b1e8e04dbed2dbec6f89af7ff2427eafe24 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:16:29 -0700 Subject: [PATCH 04/97] docs: record final native audit verification --- .../code-graph-intelligence-audit-2026-09-26.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 9207fd1e9..23ce26b7a 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -126,18 +126,18 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c - CLI query contract suite: 34/34 passed; product suite: 9/9 passed. - Query relevance qualification: 5/5 passed, including the 500 synthetic cases. - Workspace Clippy (`--workspace --lib --bins --locked -- -D warnings`): passed - after the explicit-kind and bounded-trail corrections. + after all seven production corrections. - Rust formatting check: passed. - Product boundary script: passed; competitor tooling stays outside production. -- Workspace native tests (`--workspace --lib --bins --locked`): 1,081 passed, - zero failed, two ignored at the six-defect checkpoint, including both MCP - regressions. Workspace tests/Clippy are being rerun after the extensionless - source correction and its additional MCP regression. +- Workspace native tests (`--workspace --lib --bins --locked`): 1,082 passed, + zero failed, two ignored after all seven production corrections, including + the three new MCP regressions. - Code-graph fixture qualification: initial native stages passed; the React oracle then failed because locked TypeScript dependencies were absent. After `npm ci --ignore-scripts`, the complete final gate passed (exit 0), including deterministic production updates, semantic/topology assertions, - Markdown quality, and independent React source-anchor checks. + Markdown quality, and independent React source-anchor checks at the six-defect + checkpoint. The gate is running again after the extensionless-source fix. - First v2 replay: complete but invalidated for comparative scoring (see below). - Corrected v2 replay `v2-corrected-02`: complete, after all three evaluation corrections. It uses the debug binary and recorded source patch from before @@ -242,7 +242,7 @@ source-reviewed corpus. ## Next evidence to collect -1. Finish the native rerun for the extensionless-source correction; keep the +1. Finish the fixture gate for the extensionless-source correction; keep the production build and exact source provenance with each comparison checkpoint. 2. Expand hub review beyond candidate eligibility to source-reviewed design judgments, separating connectivity from responsibility/cohesion defects. From 4befc05e31db3f6da7d08e3b8df1cf94f06dff13 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:37:13 -0700 Subject: [PATCH 05/97] fix: preserve bounded weighted paths and enforce search work limits --- .github/workflows/compass-ci.yml | 8 + CHANGELOG.md | 3 + COMPATIBILITY.md | 6 + MIGRATION.md | 10 +- crates/compass-cli/tests/code_query_cli.rs | 73 ++++++- crates/compass-query/src/traversal.rs | 184 ++++++++++++---- .../tests/bounded_path_oracle.rs | 196 ++++++++++++++++++ crates/compass-query/tests/coverage_paths.rs | 33 ++- ...ode-graph-intelligence-audit-2026-09-26.md | 66 +++++- docs/reference/commands.md | 9 +- 10 files changed, 533 insertions(+), 55 deletions(-) create mode 100644 crates/compass-query/tests/bounded_path_oracle.rs diff --git a/.github/workflows/compass-ci.yml b/.github/workflows/compass-ci.yml index 9c85dc18f..1a8fbbafe 100644 --- a/.github/workflows/compass-ci.yml +++ b/.github/workflows/compass-ci.yml @@ -105,6 +105,14 @@ jobs: # agent-view contracts rot silently. cargo test -p compass-cli --tests --locked + - name: Hub and bounded path regressions + run: | + cargo test -p compass-graph --test analyze_coverage --locked + cargo test -p compass-query --test bounded_path_oracle --test code_traversal --test coverage_paths --locked + + - name: Comparison scorer regressions (no competitor installation) + run: python3 -m unittest discover -s benchmarks/agent_query/tests + - name: Qualify natural-language query relevance env: CARGO_TARGET_DIR: ${{ github.workspace }}/target diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cd88a361..4d40680af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Fix weighted `path --max-depth` searches losing a feasible shorter prefix. + Search work exhaustion now fails explicitly instead of appearing disconnected. + - Fix bounded weighted node trails: retain shorter prefixes when a cheaper route exhausts the hop limit, and never admit a previously rejected node without charging the traversal budget. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index ad6e32c1d..96e8cdae2 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -96,6 +96,12 @@ history profiles, and cache identities. ### Bounded node trails +The undirected `path` command also retains nondominated cost/depth states. +Each weighted or alternative search is bounded to 1,000,000 adjacency entries +and 16 MiB of cumulative path-key bytes. Exceeding either returns a nonzero +work-limit error, never `NO PATH FOUND`. Previously expensive requests may now +need a smaller depth or graph. Relation weights and output schemas are unchanged. + Typed `node`/node-trail queries keep nondominated arrivals by node and depth, so a cheaper but longer prefix cannot hide a valid trail within `max_depth`. Rejected nodes are not considered admitted on a later visit. Existing cost diff --git a/MIGRATION.md b/MIGRATION.md index 7f5253674..1619fe40a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -20,8 +20,14 @@ with a suggested exact ID when exact identity is required. `compass path` no longer promotes a fuzzy symbol candidate into an endpoint; it is weighted toward structural relations, defaults to an eight-hop bound, and reports `NO PATH FOUND` separately when both endpoints exist but are -unreachable. Consumers that parsed the prior human path prose should migrate to -these explicit signals; machine-query schema versions are unchanged. +unreachable within the requested hop bound. Consumers that parsed the prior +human path prose should migrate to these explicit signals; machine-query schema +versions are unchanged. + +`path` now fails with a nonzero work-limit error if either its weighted or +alternative search exceeds 1,000,000 adjacency entries or 16 MiB of cumulative +path-key bytes. Treat this as an incomplete search, not proof of disconnection; +reduce `--max-depth` or query a smaller graph before retrying. Typed relationship commands now share source-backed import/reference resolution. `callers`, `impact`, and typed `affected` may therefore return diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 60d5a380f..743ea8435 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -102,7 +102,8 @@ fn typed_query_commands_share_the_versioned_json_contract() -> Result<(), Box Result<(), Box> { +fn node_and_path_commands_retain_the_route_that_fits_the_requested_depth() +-> Result<(), Box> { let directory = tempfile::tempdir()?; let graph_path = support::write_typed_graph(directory.path())?; let mut graph = GraphDocument::load(&graph_path)?; @@ -157,7 +158,7 @@ fn node_command_retains_the_route_that_fits_the_requested_depth() -> Result<(), OsString::from("n:s"), OsString::from("n:t"), OsString::from("--graph"), - graph_path.into_os_string(), + graph_path.clone().into_os_string(), OsString::from("--cache"), directory.path().join("cache").into_os_string(), OsString::from("--max-depth"), @@ -171,6 +172,74 @@ fn node_command_retains_the_route_that_fits_the_requested_depth() -> Result<(), serde_json::from_str(&outcome.stdout)?; assert_eq!(response.paths.len(), 1); assert_eq!(response.paths[0].node_ids, ["n:s", "n:b", "n:t"]); + let legacy = run( + Frontend::Compass, + [ + OsString::from("path"), + OsString::from("n:s"), + OsString::from("n:t"), + OsString::from("--graph"), + graph_path.into_os_string(), + OsString::from("--max-depth"), + OsString::from("2"), + ], + ); + assert_eq!(legacy.code, 0, "{}", legacy.stderr); + assert!( + legacy + .stdout + .contains("Best path (weighted, 2 hops, weight 5)"), + "{}", + legacy.stdout + ); + assert!(!legacy.stdout.contains("NO PATH FOUND")); + Ok(()) +} + +#[test] +fn path_work_limit_is_a_failed_command_not_a_no_path_answer() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = directory.path().join("graph.json"); + let ids = (0..96) + .map(|index| format!("node-{index}-{}", "x".repeat(4096))) + .collect::>(); + let nodes = ids + .iter() + .enumerate() + .map(|(index, id)| { + serde_json::json!({ + "id": id, "label": format!("Node{index}") + }) + }) + .collect::>(); + let links = ids.windows(2).enumerate().map(|(index, pair)| serde_json::json!({ + "id": format!("edge-{index}"), "source": pair[0], "target": pair[1], "relation": "calls" + })).collect::>(); + std::fs::write( + &graph_path, + serde_json::to_vec(&serde_json::json!({ + "directed":true, "nodes": nodes, "links": links + }))?, + )?; + let outcome = run( + Frontend::Compass, + [ + OsString::from("path"), + OsString::from("Node0"), + OsString::from("Node95"), + OsString::from("--graph"), + graph_path.into_os_string(), + OsString::from("--max-depth"), + OsString::from("95"), + ], + ); + assert_ne!(outcome.code, 0); + assert!( + outcome.stderr.contains("path search work limit exceeded"), + "{}", + outcome.stderr + ); + assert!(outcome.stdout.is_empty(), "{}", outcome.stdout); Ok(()) } diff --git a/crates/compass-query/src/traversal.rs b/crates/compass-query/src/traversal.rs index 8465b7c11..217f590fe 100644 --- a/crates/compass-query/src/traversal.rs +++ b/crates/compass-query/src/traversal.rs @@ -324,7 +324,8 @@ pub fn render_shortest_path_with_limit( target.index, max_depth, PathRanking::Weighted, - ); + PathSearchBudget::default(), + )?; let Some(path) = weighted.path else { return Ok(format!( "Source resolved: {}\nTarget resolved: {}\nNO PATH FOUND to resolved target (depth limit {max_depth}, {} nodes visited)", @@ -355,7 +356,8 @@ pub fn render_shortest_path_with_limit( target.index, max_depth, PathRanking::Hops, - ); + PathSearchBudget::default(), + )?; if let Some(alternative) = shorter.path && alternative.edges != path.edges && alternative.nodes.len() < path.nodes.len() @@ -542,76 +544,128 @@ struct WeightedGraphPath { weight: u32, } +// Depth-aware search keeps multiple arrivals per node. Bound both the graph +// work and the cumulative string allocation used for deterministic tie keys. +// Exhaustion is an error, never evidence that the endpoints are disconnected. +struct PathSearchBudget { + adjacency_entries: usize, + key_bytes: usize, +} + +impl Default for PathSearchBudget { + fn default() -> Self { + Self { + adjacency_entries: 1_000_000, + key_bytes: 16 * 1024 * 1024, + } + } +} + +fn path_work_limit() -> String { + "path search work limit exceeded; reduce --max-depth or use a smaller graph".to_owned() +} + fn ranked_path_undirected( graph: &Graph, source: NodeIndex, target: NodeIndex, max_depth: usize, ranking: PathRanking, -) -> GraphPathResult { + mut budget: PathSearchBudget, +) -> Result { + type State = (NodeIndex, u32); let source_id = graph.node(source).id.clone(); + budget.key_bytes = budget + .key_bytes + .checked_sub(source_id.len()) + .ok_or_else(path_work_limit)?; let mut queue = BinaryHeap::from([Reverse((0_u32, 0_u32, source_id.clone(), source))]); - let mut best = BTreeMap::from([(source, (0_u32, 0_u32, source_id))]); - let mut predecessor = BTreeMap::::new(); + let mut best = BTreeMap::from([(source, BTreeMap::from([(0_u32, (0_u32, source_id))]))]); + let mut predecessor = BTreeMap::::new(); let mut visited = BTreeSet::new(); + let mut target_state = None; while let Some(Reverse((primary, secondary, path_key, node))) = queue.pop() { - if best.get(&node).is_none_or(|current| { - current.0 != primary || current.1 != secondary || current.2 != path_key - }) { + let (weight, hops) = match ranking { + PathRanking::Weighted => (primary, secondary), + PathRanking::Hops => (secondary, primary), + }; + let Some(labels) = best.get(&node) else { + continue; + }; + if labels + .get(&hops) + .is_none_or(|current| current.0 != weight || current.1 != path_key) + || labels.range(..hops).any(|(_, current)| current.0 <= weight) + { continue; } + let state = (node, hops); visited.insert(node); if node == target { + target_state = Some(state); break; } - let hops = match ranking { - PathRanking::Weighted => secondary, - PathRanking::Hops => primary, - }; if usize::try_from(hops).unwrap_or(usize::MAX) >= max_depth { continue; } - for (neighbor, edge_index, weight, edge_key) in graph_adjacency(graph, node) { + for (neighbor, edge_index, edge_weight, edge_key) in + graph_adjacency(graph, node, &mut budget)? + { let next_hops = hops.saturating_add(1); - let current_weight = match ranking { - PathRanking::Weighted => primary, - PathRanking::Hops => secondary, - }; - let next_weight = current_weight.saturating_add(weight); + let next_weight = weight.saturating_add(edge_weight); + // Reject dominated arrivals (including positive-cost cycles) + // before allocating a path key. Only equal-cost/depth ties need + // the lexical comparison below. + if best.get(&neighbor).is_some_and(|labels| { + labels.range(..=next_hops).any(|(depth, current)| { + current.0 < next_weight || (current.0 == next_weight && *depth < next_hops) + }) + }) { + continue; + } let (next_primary, next_secondary) = match ranking { PathRanking::Weighted => (next_weight, next_hops), PathRanking::Hops => (next_hops, next_weight), }; + let next_length = path_key + .len() + .checked_add(edge_key.len()) + .and_then(|length| length.checked_add(graph.node(neighbor).id.len())) + .and_then(|length| length.checked_add(2)) + .ok_or_else(path_work_limit)?; + budget.key_bytes = budget + .key_bytes + .checked_sub(next_length) + .ok_or_else(path_work_limit)?; let next_key = format!("{path_key}\0{edge_key}\0{}", graph.node(neighbor).id); - let candidate = (next_primary, next_secondary, next_key.clone()); if best .get(&neighbor) - .is_none_or(|current| candidate < current.clone()) + .and_then(|labels| labels.get(&next_hops)) + .is_some_and(|current| current.0 == next_weight && current.1 <= next_key) { - best.insert(neighbor, candidate); - predecessor.insert(neighbor, (node, edge_index)); - queue.push(Reverse((next_primary, next_secondary, next_key, neighbor))); + continue; } + best.entry(neighbor) + .or_default() + .insert(next_hops, (next_weight, next_key.clone())); + predecessor.insert((neighbor, next_hops), (state, edge_index)); + queue.push(Reverse((next_primary, next_secondary, next_key, neighbor))); } } - if !best.contains_key(&target) || !visited.contains(&target) { - return GraphPathResult { + let Some(mut cursor) = target_state else { + return Ok(GraphPathResult { path: None, visited_nodes: visited.len(), - }; - } + }); + }; let mut nodes = vec![target]; let mut edges = Vec::new(); - let mut cursor = target; - while cursor != source { + while cursor != (source, 0) { let Some((previous, edge)) = predecessor.get(&cursor).copied() else { - return GraphPathResult { - path: None, - visited_nodes: visited.len(), - }; + return Err("path predecessor state is missing".to_owned()); }; edges.push(edge); - nodes.push(previous); + nodes.push(previous.0); cursor = previous; } nodes.reverse(); @@ -620,21 +674,31 @@ fn ranked_path_undirected( .iter() .map(|edge| relation_weight(&graph.edge(*edge).string("relation"))) .fold(0_u32, u32::saturating_add); - GraphPathResult { + Ok(GraphPathResult { path: Some(WeightedGraphPath { nodes, edges, weight, }), visited_nodes: visited.len(), - } + }) } -fn graph_adjacency(graph: &Graph, node: NodeIndex) -> Vec<(NodeIndex, EdgeIndex, u32, String)> { - let edge_indices = graph - .outgoing_edges(node) - .chain(graph.incoming_edges(node)) - .collect::>(); +type PathAdjacency = (NodeIndex, EdgeIndex, u32, String); + +fn graph_adjacency( + graph: &Graph, + node: NodeIndex, + budget: &mut PathSearchBudget, +) -> Result, String> { + let mut edge_indices = BTreeSet::new(); + for edge in graph.outgoing_edges(node).chain(graph.incoming_edges(node)) { + budget.adjacency_entries = budget + .adjacency_entries + .checked_sub(1) + .ok_or_else(path_work_limit)?; + edge_indices.insert(edge); + } let mut adjacent = Vec::with_capacity(edge_indices.len()); for edge_index in edge_indices.iter().copied() { let Some((source, target)) = graph.edge_endpoints(edge_index) else { @@ -657,7 +721,7 @@ fn graph_adjacency(graph: &Graph, node: NodeIndex) -> Vec<(NodeIndex, EdgeIndex, .then_with(|| graph.node(left.0).id.cmp(&graph.node(right.0).id)) .then_with(|| left.3.cmp(&right.3)) }); - adjacent + Ok(adjacent) } fn relation_weight(relation: &str) -> u32 { @@ -1549,6 +1613,42 @@ mod tests { use super::dfs; + #[test] + fn path_work_exhaustion_is_an_error_not_a_disconnected_result() + -> Result<(), Box> { + let graph = Graph::from_document(serde_json::from_value::(json!({ + "directed": true, + "nodes": [{"id":"seed", "label":"Seed"}, {"id":"target", "label":"Target"}], + "links": [{"source":"seed", "target":"target", "relation":"calls"}] + }))?)?; + let seed = graph.node_index("seed").ok_or("missing seed")?; + let target = graph.node_index("target").ok_or("missing target")?; + for budget in [ + super::PathSearchBudget { + adjacency_entries: 0, + ..Default::default() + }, + super::PathSearchBudget { + key_bytes: 4, + ..Default::default() + }, + ] { + let error = super::ranked_path_undirected( + &graph, + seed, + target, + 2, + super::PathRanking::Weighted, + budget, + ) + .err() + .ok_or("expected work-limit error")?; + assert!(error.contains("path search work limit exceeded")); + assert!(!error.contains("NO PATH FOUND")); + } + Ok(()) + } + #[test] fn dfs_edges_always_reference_visited_nodes_at_depth_cap() -> Result<(), Box> { diff --git a/crates/compass-query/tests/bounded_path_oracle.rs b/crates/compass-query/tests/bounded_path_oracle.rs new file mode 100644 index 000000000..a58871ca9 --- /dev/null +++ b/crates/compass-query/tests/bounded_path_oracle.rs @@ -0,0 +1,196 @@ +mod support; + +use std::error::Error; + +use compass_model::code_graph::{EdgeKind, GraphDocument, NodeKind}; +use compass_model::identity::edge_id; +use compass_model::query_contract::{CodeQueryLimits, NodeTrailRequest}; +use compass_query::{open_with_document, render_shortest_path_with_limit}; + +const PAIRS: [(usize, usize); 6] = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]; +type Matrix = [[Option; 4]; 4]; + +// Independent enumeration of all simple paths. Positive weights mean a cycle +// cannot improve either cost or hop count. This oracle does not reuse the +// production queue, dominance rules, relation-weight mapping, or predecessor map. +fn oracle(matrix: &Matrix, max_depth: usize) -> Option<(u32, usize)> { + fn visit(matrix: &Matrix, node: usize, remaining: usize, visited: u8) -> Option<(u32, usize)> { + if node == 3 { + return Some((0, 0)); + } + if remaining == 0 { + return None; + } + (0..4) + .filter(|next| visited & (1 << next) == 0) + .filter_map(|next| { + let edge = matrix[node][next]?; + let (cost, hops) = visit(matrix, next, remaining - 1, visited | (1 << next))?; + Some((edge + cost, 1 + hops)) + }) + .min() + } + visit(matrix, 0, max_depth, 1) +} + +#[test] +fn both_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = directory.path().join("graph.json"); + support::write_graph(&graph_path)?; + let mut template = GraphDocument::load(&graph_path)?; + let node_pattern = regex::Regex::new(r"Node([0-3])")?; + let edge_pattern = regex::Regex::new( + r"--(calls|references)(?: \[[^\]]+\])?-->|<--(calls|references)(?: \[[^\]]+\])?--", + )?; + let edge_template = template + .links + .first() + .cloned() + .ok_or("missing edge template")?; + template.nodes = (0..4) + .map(|i| { + support::node( + &format!("n:{i}"), + NodeKind::Function, + &format!("Node{i}"), + &format!("Node{i}"), + ) + }) + .collect(); + + // Every one of the six forward pairs is absent, a cost-1 call, or a cost-4 + // reference: 729 graphs. The typed engine sees DAGs; the undirected path + // engine also sees cycles. Every graph is queried at all three hop bounds. + for encoding in 0_u32..729 { + let mut graph = template.clone(); + graph.links.clear(); + let mut directed: Matrix = [[None; 4]; 4]; + let mut undirected: Matrix = [[None; 4]; 4]; + let mut digits = encoding; + for (source, target) in PAIRS { + let digit = digits % 3; + digits /= 3; + let (kind, weight) = match digit { + 1 => (EdgeKind::Calls, 1), + 2 => (EdgeKind::References, 4), + _ => continue, + }; + directed[source][target] = Some(weight); + undirected[source][target] = Some(weight); + undirected[target][source] = Some(weight); + let mut edge = edge_template.clone(); + edge.source = format!("n:{source}"); + edge.target = format!("n:{target}"); + edge.kind = kind; + edge.occurrence_rule = None; + edge.id = edge_id( + &edge.source, + kind, + &edge.target, + edge.relationship_site.as_ref(), + None, + ); + edge.key.clone_from(&edge.id); + graph.links.push(edge); + } + let legacy = compass_model::Graph::from_document(serde_json::from_value( + serde_json::to_value(&graph)?, + )?)?; + // Drop each index before the next graph so this test's disk use stays + // bounded by one tiny graph rather than accumulating 729 databases. + let cache = tempfile::tempdir_in(directory.path())?; + let engine = open_with_document(graph.clone(), &graph_path, None, cache.path())?; + for depth in 1..=3 { + let context = format!("graph={encoding}, depth={depth}"); + let output = render_shortest_path_with_limit(&legacy, "n:0", "n:3", depth)?; + if let Some((cost, hops)) = oracle(&undirected, depth) { + assert!( + output.contains(&format!( + "Best path (weighted, {hops} hops, weight {cost}):" + )), + "{context}: {output}" + ); + let body = output + .lines() + .find(|line| line.trim_start().starts_with("Node0")) + .ok_or("missing rendered path body")?; + let nodes = node_pattern + .captures_iter(body) + .map(|capture| capture[1].parse::()) + .collect::, _>>()?; + let edges = edge_pattern.captures_iter(body).collect::>(); + assert_eq!(nodes.first(), Some(&0), "{context}"); + assert_eq!(nodes.last(), Some(&3), "{context}"); + assert_eq!(nodes.len(), hops + 1, "{context}"); + assert_eq!(edges.len(), hops, "{context}"); + let mut rendered_cost = 0; + for (pair, edge) in nodes.windows(2).zip(edges) { + let (source, target, relation) = if let Some(relation) = edge.get(1) { + (pair[0], pair[1], relation.as_str()) + } else { + ( + pair[1], + pair[0], + edge.get(2).ok_or("missing relation")?.as_str(), + ) + }; + let weight = if relation == "calls" { 1 } else { 4 }; + assert_eq!(directed[source][target], Some(weight), "{context}: {body}"); + rendered_cost += weight; + } + assert_eq!(rendered_cost, cost, "{context}"); + } else { + assert!(output.contains("NO PATH FOUND"), "{context}: {output}"); + } + let response = engine.node_trail(NodeTrailRequest { + source: "n:0".to_owned(), + target: "n:3".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits { + max_depth: depth as u32, + ..CodeQueryLimits::default() + }, + })?; + assert!(!response.truncated, "{context}"); + if let Some((expected_cost, expected_hops)) = oracle(&directed, depth) { + assert_eq!(response.paths.len(), 1, "{context}"); + let path = &response.paths[0]; + assert_eq!( + path.node_ids.first().map(String::as_str), + Some("n:0"), + "{context}" + ); + assert_eq!( + path.node_ids.last().map(String::as_str), + Some("n:3"), + "{context}" + ); + assert_eq!(path.edge_ids.len(), expected_hops, "{context}"); + assert_eq!(path.node_ids.len(), expected_hops + 1, "{context}"); + let mut cost = 0; + for (pair, edge_id) in path.node_ids.windows(2).zip(&path.edge_ids) { + let edge = graph + .links + .iter() + .find(|edge| edge.id == *edge_id) + .ok_or("path invented an edge")?; + assert_eq!( + (&edge.source, &edge.target), + (&pair[0], &pair[1]), + "{context}" + ); + cost += match edge.kind { + EdgeKind::Calls => 1, + EdgeKind::References => 4, + _ => return Err("unexpected edge kind".into()), + }; + } + assert_eq!(cost, expected_cost, "{context}"); + } else { + assert!(response.paths.is_empty(), "{context}"); + } + } + } + Ok(()) +} diff --git a/crates/compass-query/tests/coverage_paths.rs b/crates/compass-query/tests/coverage_paths.rs index 5969b66e9..91c58eafe 100644 --- a/crates/compass-query/tests/coverage_paths.rs +++ b/crates/compass-query/tests/coverage_paths.rs @@ -5,7 +5,8 @@ use compass_model::{Graph, GraphDocument}; use compass_query::{ TraversalMode, affected_nodes, find_node, format_affected, format_benchmark, normalize_context_filters, query_graph_text, query_terms, render_explanation, - render_shortest_path, resolve_seed, run_benchmark, sanitize_label, score_nodes, search_tokens, + render_shortest_path, render_shortest_path_with_limit, resolve_seed, run_benchmark, + sanitize_label, score_nodes, search_tokens, }; use serde_json::{Map, Value, json}; @@ -36,6 +37,36 @@ const FIXTURE: &str = r#"{ ] }"#; +#[test] +fn weighted_path_retains_shorter_prefixes_within_the_hop_limit() -> Result<(), Box> { + let graph = graph( + r#"{ + "directed":true, + "nodes":[{"id":"s","label":"Start"},{"id":"a","label":"First"}, + {"id":"b","label":"Join"},{"id":"t","label":"Target"}], + "links":[{"source":"s","target":"a","relation":"calls"}, + {"source":"a","target":"b","relation":"calls"}, + {"source":"s","target":"b","relation":"references"}, + {"source":"b","target":"t","relation":"calls"}] + }"#, + )?; + let shallow = render_shortest_path_with_limit(&graph, "s", "t", 2)?; + assert!( + shallow.contains("Best path (weighted, 2 hops, weight 5)"), + "{shallow}" + ); + assert!( + shallow.contains("Start --references--> Join --calls--> Target"), + "{shallow}" + ); + let deep = render_shortest_path_with_limit(&graph, "s", "t", 3)?; + assert!( + deep.contains("Best path (weighted, 3 hops, weight 3)"), + "{deep}" + ); + Ok(()) +} + #[test] fn affected_resolution_covers_ids_labels_sources_members_and_misses() -> Result<(), Box> { diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 23ce26b7a..8e7fe34c3 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -73,6 +73,23 @@ budget. Positive-cost cycles are dominated rather than repeatedly expanded. All search labels and predecessor records remain bounded by examined edges. The CLI contract regression also passed, within the 34-test CLI query suite. +A third navigation defect was then reproduced in the separate undirected +`path` implementation: it retained only one arrival per node and made the same +incorrect no-path claim under a two-hop bound. The release CLI and a native +regression both reproduced it. That implementation now also keeps cost/depth +states and reconstructs their exact predecessors. Its two ranking passes each +cap adjacency work at 1,000,000 entries and cumulative path keys at 16 MiB; +exhaustion is a command error, not a no-path claim. + +An independent oracle enumerates all simple paths through 729 four-node graphs, +where each of six pairs is absent, a cost-1 call, or a cost-4 reference. Both +engines are checked at depths one through three (4,374 queries). The directed +engine sees DAGs; the undirected engine also sees cycles. The oracle compares +reachability, minimum cost and hop count, and validates rendered/native edge +chains and directions. It reproduced the legacy defect at graph 113, depth 2. +This is exhaustive coverage of that small family, not a general graph proof or +independent real-source extraction evaluation. + ## Evaluation corrections The v1 graph-anchor scorer ignored the requested symbol. For Compass, any @@ -123,21 +140,29 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c - Graph analysis integration suite: 12/12 passed after all five hub fixes. - Benchmark Python unit suite: 38/38 passed, including ten path-auditor tests. -- CLI query contract suite: 34/34 passed; product suite: 9/9 passed. +- CLI query contract suite: 35/35 passed; product suite: 9/9 passed. +- After the separate legacy `path` fix: query library 178/178, exhaustive oracle + 1/1 (4,374 queries), traversal integration 11/11, legacy query coverage 6/6, + CLI query suite 35/35 and product suite 9/9 passed. This includes an actual + work-limit CLI failure with empty stdout. These graph/path regressions and + the competitor-free Python scorer tests are now explicitly wired into CI. - Query relevance qualification: 5/5 passed, including the 500 synthetic cases. - Workspace Clippy (`--workspace --lib --bins --locked -- -D warnings`): passed - after all seven production corrections. + after all eight production corrections. The new query integration tests also + pass a dedicated Clippy run with warnings denied. - Rust formatting check: passed. - Product boundary script: passed; competitor tooling stays outside production. -- Workspace native tests (`--workspace --lib --bins --locked`): 1,082 passed, - zero failed, two ignored after all seven production corrections, including +- Workspace native tests (`--workspace --lib --bins --locked`): 1,083 passed, + zero failed, two ignored after all eight production corrections, including the three new MCP regressions. - Code-graph fixture qualification: initial native stages passed; the React oracle then failed because locked TypeScript dependencies were absent. After `npm ci --ignore-scripts`, the complete final gate passed (exit 0), including deterministic production updates, semantic/topology assertions, Markdown quality, and independent React source-anchor checks at the six-defect - checkpoint. The gate is running again after the extensionless-source fix. + checkpoint. The complete gate also passed at the seven-defect checkpoint + after the extensionless-source fix. The built release binary still reproduces + the separate legacy `path` defect; that run is not evidence for the eighth fix. - First v2 replay: complete but invalidated for comparative scoring (see below). - Corrected v2 replay `v2-corrected-02`: complete, after all three evaluation corrections. It uses the debug binary and recorded source patch from before @@ -148,6 +173,11 @@ available Graphify source checkout is at `26b02b5e3430e4ab85dd7e72c7b98836d8e65c source-grounded path audit passes 5/5 per tool. All 92 previously recorded Graphify source/data file hashes remain unchanged after the run; this does not pin every transitive dependency. +- Explicit fixed-graph regression replay `query-only-04`: the current debug + query binary passes all 50 text oracles on the retained `v2-release-03` + Compass graphs and all five source-grounded path witnesses. Source state and + graph hashes were verified before/after querying. This checks query + compatibility; it is not a new extraction or comparative performance run. ## Findings from the first fresh replay @@ -240,10 +270,32 @@ and captured responses. It currently audits successful positive path rows; unreachable, ambiguous, truncated and limit outcomes still need a broader source-reviewed corpus. +## Synthetic boundary diagnostics + +Separate shared-graph cases were recorded before executing either tool, with +raw outputs and exact arguments retained under `path-boundary-diagnostic` in +the evaluation workspace. These isolate query behavior from extraction quality. + +| Case | Compass release checkpoint | Graphify 0.9.67 | +| --- | --- | --- | +| Two disconnected components | Explicit no path | Explicit no path | +| Absent endpoint | Nonzero no-match error | Nonzero no-match error | +| Two `Worker` nodes in different components | Lists both IDs and refuses a path | Warns on stderr, selects one, returns its path | +| Exact ID in the disconnected component | Explicit no path | Explicit no path | +| Reverse traversal of a stored call | Preserves reverse arrow | Preserves reverse arrow | +| Both endpoints resolve to the same node | Explicit refusal | Explicit refusal | + +The ambiguity row records a policy difference: Graphify does disclose the +ambiguity, but still picks an endpoint. It must not be described as hiding the +warning. The two hop-limit cases are Compass-only contract checks because +Graphify exposes no documented hop-bound option. A one-hop request correctly +returns a bounded no-path result; the two-hop request reproduced the third +navigation defect above. These are not extra head-to-head accuracy wins. + ## Next evidence to collect -1. Finish the fixture gate for the extensionless-source correction; keep the - production build and exact source provenance with each comparison checkpoint. +1. Keep exact build/source provenance for subsequent release comparisons; + the latest query correction has native and fixed-graph regression evidence. 2. Expand hub review beyond candidate eligibility to source-reviewed design judgments, separating connectivity from responsibility/cohesion defects. 3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 95801c900..8db7ad6d5 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -550,12 +550,19 @@ any graph search; missing and ambiguous endpoints fail explicitly. The text path search is bounded to eight hops by default and ranks structural relationships such as calls, containment, imports, and dependencies ahead of weak references or documentation links. When a meaningfully weaker route is up to two hops -shorter, Compass shows it separately. Output names the resolved target ID, and +shorter, Compass shows it separately. Output names the resolved target, and an unreachable target is reported as `NO PATH FOUND` with the depth bound and visited-node count. Relationship arrows always preserve their stored direction. Traversal may follow a relationship in either direction; the arrows make that choice visible rather than rewriting the graph. +Costlier, shorter prefixes are retained when they can reach the target within +`--max-depth`. Each weighted or alternative search permits at most 1,000,000 +adjacency entries and 16 MiB of cumulative path-key bytes. Work exhaustion +returns a nonzero error; it is not reported as `NO PATH FOUND`. Retry with a +smaller depth or graph. A depth-bounded no-path result does not prove that no +longer path exists. + Both endpoints accept a file path as well as a symbol. When a language publishes an isolated metadata `file` node beside the `module` node that carries the file's contents, an isolated file endpoint resolves to the single From fe1a1fb5c9eaa47388e7bdf657ddecf16b727b31 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:42:21 -0700 Subject: [PATCH 06/97] test: preregister source-first fd query and call witnesses --- benchmarks/agent_query/edge_witnesses_fd.json | 326 ++++++++++++++++++ benchmarks/agent_query/suite_fd.toml | 168 +++++++++ 2 files changed, 494 insertions(+) create mode 100644 benchmarks/agent_query/edge_witnesses_fd.json create mode 100644 benchmarks/agent_query/suite_fd.toml diff --git a/benchmarks/agent_query/edge_witnesses_fd.json b/benchmarks/agent_query/edge_witnesses_fd.json new file mode 100644 index 000000000..c94b7b631 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_fd.json @@ -0,0 +1,326 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "fd", + "commit": "b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5", + "scope": "Source-first selected direct-call witnesses recorded before extracting or querying either tool on this checkout. Covers named declarations and line-level occurrences, not whole-repository recall, runtime execution, column accuracy, or representative held-out accuracy.", + "witnesses": [ + { + "id": "execute-commands", + "source": { + "file": "src/exec/mod.rs", + "line": 76, + "symbol": "execute", + "text": "pub fn execute(" + }, + "target": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 87, + "text": "execute_commands(commands," + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "command-error", + "source": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "target": { + "file": "src/exec/command.rs", + "line": 101, + "symbol": "handle_cmd_error", + "text": "pub fn handle_cmd_error(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/command.rs", + "line": 68, + "text": "handle_cmd_error(None, e)" + }, + { + "file": "src/exec/command.rs", + "line": 93, + "text": "handle_cmd_error(Some(&cmd), why)" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "batch-error", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/command.rs", + "line": 101, + "symbol": "handle_cmd_error", + "text": "pub fn handle_cmd_error(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 105, + "text": "handle_cmd_error(Some(&builder.cmd), e)" + }, + { + "file": "src/exec/mod.rs", + "line": 112, + "text": "handle_cmd_error(Some(&builder.cmd), e)" + }, + { + "file": "src/exec/mod.rs", + "line": 118, + "text": "handle_cmd_error(None, e)" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "batch-exitcodes", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exit_codes.rs", + "line": 46, + "symbol": "merge_exitcodes", + "text": "pub fn merge_exitcodes(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 116, + "text": "merge_exitcodes(builders.iter()" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "builder-push-finish", + "source": { + "file": "src/exec/mod.rs", + "line": 173, + "symbol": "push", + "text": "fn push(" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 191, + "symbol": "finish", + "text": "fn finish(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 175, + "text": "self.finish()?" + }, + { + "file": "src/exec/mod.rs", + "line": 183, + "text": "self.finish()?" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "builder-finish-new-command", + "source": { + "file": "src/exec/mod.rs", + "line": 191, + "symbol": "finish", + "text": "fn finish(" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 164, + "symbol": "new_command", + "text": "fn new_command(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 198, + "text": "Self::new_command(&self.pre_args)?" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "size-parse", + "source": { + "file": "src/filter/size.rs", + "line": 28, + "symbol": "from_string", + "text": "pub fn from_string(" + }, + "target": { + "file": "src/filter/size.rs", + "line": 33, + "symbol": "parse_opt", + "text": "fn parse_opt(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/filter/size.rs", + "line": 29, + "text": "SizeFilter::parse_opt(s)" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "time-before-parse", + "source": { + "file": "src/filter/time.rs", + "line": 49, + "symbol": "before", + "text": "pub fn before(" + }, + "target": { + "file": "src/filter/time.rs", + "line": 29, + "symbol": "from_str", + "text": "fn from_str(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/filter/time.rs", + "line": 50, + "text": "TimeFilter::from_str(s)" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "command-buffer-write", + "source": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "target": { + "file": "src/exec/command.rs", + "line": 30, + "symbol": "write", + "text": "fn write(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/command.rs", + "line": 87, + "text": "output_buffer.write();" + }, + { + "file": "src/exec/command.rs", + "line": 92, + "text": "output_buffer.write();" + }, + { + "file": "src/exec/command.rs", + "line": 97, + "text": "output_buffer.write();" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "command-buffer-push", + "source": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "target": { + "file": "src/exec/command.rs", + "line": 26, + "symbol": "push", + "text": "fn push(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/command.rs", + "line": 84, + "text": "output_buffer.push(output.stdout, output.stderr);" + } + ], + "judgment": "Each listed line is an explicit call in the reviewed caller body." + }, + { + "id": "wrong-push-owner", + "source": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 173, + "symbol": "push", + "text": "fn push(" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "The caller body at command.rs:60-99 invokes OutputBuffer::push, not CommandBuilder::push." + }, + { + "id": "finish-not-executor", + "source": { + "file": "src/exec/mod.rs", + "line": 191, + "symbol": "finish", + "text": "fn finish(" + }, + "target": { + "file": "src/exec/command.rs", + "line": 60, + "symbol": "execute_commands", + "text": "pub fn execute_commands<" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "The complete body at mod.rs:191-203 contains no direct execute_commands call." + } + ] +} diff --git a/benchmarks/agent_query/suite_fd.toml b/benchmarks/agent_query/suite_fd.toml new file mode 100644 index 000000000..cad7458e9 --- /dev/null +++ b/benchmarks/agent_query/suite_fd.toml @@ -0,0 +1,168 @@ +# Source-first diagnostic sample, registered before querying either tool on fd. +# This is one additional Rust repository, not a representative held-out corpus. +# Text matching remains a recall proxy; edge_witnesses_fd.json separately +# records relationship identity and every reviewed occurrence for selected calls. +# Default query forms are used on both tools. Undirected path semantics match. +# Source excerpts are reported separately as a feature capability. +schema = "compass.agent-query-suite/1" + +[[repository]] +name = "fd" +language = "Rust" +url = "https://github.com/sharkdp/fd.git" +commit = "b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5" + +[[repository.anchor]] +file = "src/exec/command.rs" +line = 60 +symbol = "execute_commands" +judgment = "The generic free function is declared at line 60." + +[[repository.anchor]] +file = "src/exec/mod.rs" +line = 51 +symbol = "new_batch" +judgment = "CommandSet::new_batch begins at line 51." + +[[repository.anchor]] +file = "src/exec/command.rs" +line = 101 +symbol = "handle_cmd_error" +judgment = "The free error handler is declared at line 101." + +[[repository.anchor]] +file = "src/filter/size.rs" +line = 28 +symbol = "from_string" +judgment = "SizeFilter::from_string starts at line 28; OwnerFilter has another method with this name." + +[[repository.anchor]] +file = "src/exec/mod.rs" +line = 191 +symbol = "finish" +judgment = "CommandBuilder::finish begins at line 191." + +[[repository.question]] +id = "fd-explain-execute-commands" +kind = "explain" +subject = "execute_commands declaration" +compass = ["explain", "execute_commands"] +graphify = ["explain", "execute_commands()"] +expect = "answer" +required = ["src/exec/command.rs", "60"] +judgment = "src/exec/command.rs:60 declares execute_commands." + +[[repository.question]] +id = "fd-explain-new-batch" +kind = "explain" +subject = "CommandSet::new_batch declaration" +compass = ["explain", "new_batch"] +graphify = ["explain", ".new_batch()"] +expect = "answer" +required = ["src/exec/mod.rs", "51"] +judgment = "src/exec/mod.rs:51 declares the unique new_batch method." + +[[repository.question]] +id = "fd-source-handle-error" +kind = "explain_source" +subject = "handle_cmd_error declaration source" +compass = ["explain", "handle_cmd_error", "--source", "--root", "."] +graphify = ["explain", "handle_cmd_error()"] +expect = "answer" +required = ["pub fn handle_cmd_error(cmd: Option<&Command>, err: io::Error) -> ExitCode"] +judgment = "The requested declaration text is src/exec/command.rs:101." + +[[repository.question]] +id = "fd-callers-execute-commands" +kind = "callers" +subject = "direct callers of execute_commands" +compass = ["callers", "execute_commands"] +graphify = ["affected", "execute_commands()", "--depth", "1"] +expect = "answer" +required = ["execute", "src/exec/mod.rs"] +judgment = "CommandSet::execute at mod.rs:76 calls execute_commands at mod.rs:87." + +[[repository.question]] +id = "fd-callees-execute-commands" +kind = "callees" +subject = "internal callees of execute_commands" +compass = ["callees", "execute_commands"] +graphify = ["explain", "execute_commands()"] +expect = "answer" +required = ["handle_cmd_error", "write"] +judgment = "execute_commands calls handle_cmd_error at lines 68 and 93, and OutputBuffer::write at lines 87, 92, 97 of command.rs." + +[[repository.question]] +id = "fd-callees-execute-batch" +kind = "callees" +subject = "internal callees of CommandSet::execute_batch" +compass = ["callees", "execute_batch"] +graphify = ["explain", ".execute_batch()"] +expect = "answer" +required = ["handle_cmd_error", "merge_exitcodes", "finish"] +judgment = "mod.rs:90-120 calls handle_cmd_error at 105/112/118, CommandBuilder::finish at 111, and merge_exitcodes at 116." + +[[repository.question]] +id = "fd-path-command-error" +kind = "path" +subject = "execute_commands to handle_cmd_error" +compass = ["path", "execute_commands", "handle_cmd_error"] +graphify = ["path", "execute_commands()", "handle_cmd_error()", "--undirected"] +expect = "answer" +required = ["execute_commands", "handle_cmd_error"] +forbidden = ["NO PATH FOUND", "No path found"] +judgment = "Direct calls occur at command.rs:68 and 93; a valid source-supported one-hop relationship exists." + +[[repository.question]] +id = "fd-path-batch-exitcodes" +kind = "path" +subject = "execute_batch to merge_exitcodes" +compass = ["path", "execute_batch", "merge_exitcodes"] +graphify = ["path", ".execute_batch()", "merge_exitcodes()", "--undirected"] +expect = "answer" +required = ["execute_batch", "merge_exitcodes"] +forbidden = ["NO PATH FOUND", "No path found"] +judgment = "CommandSet::execute_batch calls merge_exitcodes at mod.rs:116; the target is declared at exit_codes.rs:46." + +[[repository.question]] +id = "fd-ambiguous-new" +kind = "ambiguity" +subject = "new constructor ambiguity" +compass = ["search", "new"] +graphify = ["explain", "new"] +expect = "pick_list" +required_one_of = ["src/exec/mod.rs", "src/exec/command.rs"] +min_one_of = 1 +judgment = "CommandSet::new, CommandBuilder::new and CommandTemplate::new occur at mod.rs:36/136/220; OutputBuffer::new is command.rs:19." + +[[repository.question]] +id = "fd-negative" +kind = "negative" +subject = "absent symbol CompassAuditAbsentFdSymbol9021" +compass = ["search", "CompassAuditAbsentFdSymbol9021"] +graphify = ["explain", "CompassAuditAbsentFdSymbol9021"] +expect = "no_match" +judgment = "The exact identifier is absent from all tracked files at the pinned commit." + +[[repository.question]] +id = "fd-file-exec-exitcodes" +kind = "file_path" +subject = "src/exec/mod.rs to src/exit_codes.rs" +compass = ["path", "src/exec/mod.rs", "src/exit_codes.rs"] +graphify = ["path", "src/exec/mod.rs", "src/exit_codes.rs", "--undirected"] +expect = "answer" +required = ["mod.rs", "exit_codes"] +forbidden = ["NO PATH FOUND", "No path found"] +judgment = "mod.rs:14 imports ExitCode and merge_exitcodes; the latter is called at 116 and declared in exit_codes.rs:46." + +[[repository.question]] +id = "fd-broad-command-errors" +kind = "broad" +subject = "how are command errors handled" +compass = ["query", "how are command errors handled"] +graphify = ["query", "how are command errors handled"] +expect = "answer" +budget_tokens = 600 +max_follow_ups = 3 +required = ["handle_cmd_error"] +judgment = "handle_cmd_error at command.rs:101 branches on command-not-found and reports other execution errors." From ea527d1b38280d3e993eb3c26993db0cc570faf5 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 14:56:56 -0700 Subject: [PATCH 07/97] test: audit source-first fd relationships and preserve failures --- benchmarks/agent_query/README.md | 46 +++++- benchmarks/agent_query/edge_audit.py | 132 ++++++++++++++++++ .../edge_witnesses_fd_diagnostic.json | 127 +++++++++++++++++ benchmarks/agent_query/path_witnesses_fd.json | 80 +++++++++++ benchmarks/agent_query/runner.py | 4 +- .../agent_query/tests/test_edge_audit.py | 107 ++++++++++++++ benchmarks/agent_query/tests/test_runner.py | 33 +++++ ...ode-graph-intelligence-audit-2026-09-26.md | 104 +++++++++++++- 8 files changed, 624 insertions(+), 9 deletions(-) create mode 100644 benchmarks/agent_query/edge_audit.py create mode 100644 benchmarks/agent_query/edge_witnesses_fd_diagnostic.json create mode 100644 benchmarks/agent_query/path_witnesses_fd.json create mode 100644 benchmarks/agent_query/tests/test_edge_audit.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 71b16b1be..6ddbbe09d 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -4,12 +4,13 @@ in its suites compared with Graphify on the same pinned checkouts. It is developer-side tooling: Compass never runs it, and it never installs Graphify. -Two suites share the harness: +Three suites share the harness: | Suite | Questions | Shape | | --- | ---: | --- | | `suite.toml` | 47 | The first five-repository suite, including Compass's compact and paged projections | | `suite_v2.toml` | 50 | A blackbox-fair extension: same questions for both tools, default output forms, no tool-specific projections | +| `suite_fd.toml` | 12 | Separate pinned `sharkdp/fd` sample, recorded from source before either tool's first extraction/query run | `suite_v2.toml` states its fairness contract inline and keeps it in the rows: both tools are blackboxes over the same pinned checkout, every oracle is read @@ -37,7 +38,7 @@ The suite covers five real repositories in five languages: | `colinhacks/zod` | TypeScript | schema parse and safe-parse helpers | | `tokio-rs/axum` | Rust | routing and service dispatch | -Both suites contribute source-reviewed questions across `explain`, +The first two suites contribute source-reviewed questions across `explain`, `explain_source`, `callers`, `callees`, `impact`, `path`, `file_path`, `ambiguity`, `negative`, and `broad` (the first suite adds the `brief`, `brief_callers`, and `paged_callers` projections). Every question declares the @@ -165,11 +166,48 @@ route proves navigation only. Neither is automatically credited as a call path. The auditor currently requires successful, single-response executions of these positive rows; it does not score negative or truncated path outcomes. +### Source-first direct-call sample + +`suite_fd.toml` and `edge_witnesses_fd.json` were committed together before the +first run on `sharkdp/fd` at `b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5`. +The 12 questions and 10 positive direct-call pairs were selected by source +inspection. The positive pairs contain 16 call occurrences; two additional +pairs must be absent. This is a selected sample, not a representative held-out +corpus or an estimate of whole-repository precision/recall. + +```bash +python3 benchmarks/agent_query/runner.py run \ + --suite benchmarks/agent_query/suite_fd.toml \ + --workspace /path/to/evaluations --run-id fd-fresh \ + --compass-binary /path/to/compass --graphify-binary /path/to/graphify \ + --source fd=/path/to/pinned-clean-fd + +python3 -m benchmarks.agent_query.edge_audit \ + --run /path/to/evaluations/runs/fd-fresh \ + --witnesses benchmarks/agent_query/edge_witnesses_fd.json \ + --output /path/to/new-edge-audit.json +``` + +The edge auditor requires both exact declaration identities even for negative +pairs, checks semantic direction and relation, and separately scores relationship +presence and occurrence coverage. A duplicate at one line cannot recover a +missing occurrence at another. Source files, captured suite/graph digests, +pinned checkout state, and auditor code identity are checked. Reports record +missing and unexpected occurrences and require a new output path. Occurrence +matching covers start lines, not column accuracy or runtime execution. + +`edge_witnesses_fd_diagnostic.json` adds five **post-output** checks for the +missing loop calls and constructor-owner mistake discovered in that run. Keep +those results separate from the preregistered sample. `path_witnesses_fd.json` +likewise audits the two positive path responses after output review using the +existing path auditor's `--witnesses` option. It verifies a compatible graph +occurrence; the text path does not identify a particular parallel edge. + ### Interpretation Anchor matching is a deterministic text-recall proxy over bounded output, not -an independent precision oracle. The suite is a focused five-repository -sample; it does not estimate population-wide accuracy. Graphify prints an +an independent precision oracle. The suites are focused source-reviewed +samples; they do not estimate population-wide accuracy. Graphify prints an installation warning on stderr, which `run.json` records separately and the token metric excludes. diff --git a/benchmarks/agent_query/edge_audit.py b/benchmarks/agent_query/edge_audit.py new file mode 100644 index 000000000..a187660fb --- /dev/null +++ b/benchmarks/agent_query/edge_audit.py @@ -0,0 +1,132 @@ +"""Check selected direct relationships and call occurrences against pinned source. + +This is a diagnostic over explicitly reviewed endpoint pairs, not an estimate +of whole-graph precision or recall. Missing endpoints never pass a negative. +""" + +from __future__ import annotations + +import argparse +from collections import Counter +import hashlib +import json +from pathlib import Path + +from benchmarks.agent_query.path_audit import MAX_GRAPH_BYTES, _edge_site, check_source, read_bounded +from benchmarks.agent_query.runner import _node_anchor, _sha256_file, _verify_source, load_suite + + +def audit_edges(manifest: dict, tool: str, graph: dict, root: Path) -> list[dict]: + if tool not in ("compass", "graphify"): + raise ValueError("unsupported edge audit tool") + nodes = graph.get("nodes", []) + ids = [node.get("id") for node in nodes] + if any(not isinstance(identity, str) or not identity for identity in ids) or len(set(ids)) != len(ids): + raise ValueError("graph must have unique nonempty string node IDs") + edges = graph.get("edges") or graph.get("links") or [] + results = [] + for witness in manifest["witnesses"]: + expected = witness["expected"] + if expected not in ("present", "absent"): + raise ValueError("unsupported edge expectation") + occurrences = witness["occurrences"] + if (expected == "present") != bool(occurrences): + raise ValueError("positive witnesses need occurrences; negatives must have none") + for anchor in (witness["source"], witness["target"], *occurrences): + check_source(root, anchor) + endpoints = [] + for anchor in (witness["source"], witness["target"]): + matches = [] + for node in nodes: + file, line, names = _node_anchor(node, tool) + if (file, line) == (anchor["file"], anchor["line"]) and anchor["symbol"] in names: + matches.append(node["id"]) + endpoints.append(matches) + result = {"id": witness["id"], "tool": tool, "expected": expected, + "sourceCandidates": endpoints[0], "targetCandidates": endpoints[1], + "endpointIdentityVerified": all(len(matches) == 1 for matches in endpoints), + "expectedOccurrences": len(occurrences), "matchedOccurrences": 0, + "relationshipMatched": False, "matched": False} + if not result["endpointIdentityVerified"]: + result["reason"] = "missing or ambiguous declaration identity" + results.append(result) + continue + source, target = endpoints[0][0], endpoints[1][0] + matches = [edge for edge in edges if ( + edge.get("_src", edge.get("source")), edge.get("_tgt", edge.get("target")), + edge.get("kind", edge.get("relation"))) == (source, target, witness["relation"])] + result["matchingEdges"] = len(matches) + result["relationshipMatched"] = bool(matches) if expected == "present" else not matches + expected_sites = Counter((site["file"], site["line"]) for site in occurrences) + observed_sites = Counter(_edge_site(edge, tool) for edge in matches) + # Counter subtraction preserves multiplicity: duplicating one occurrence + # cannot recover another missing call site. + def records(sites: Counter) -> list[dict]: + return [{"file": file, "line": line, "count": count} + for (file, line), count in sorted(sites.items(), key=lambda item: repr(item[0]))] + result["expectedOccurrences"] = sum(expected_sites.values()) + result["matchedOccurrences"] = sum((expected_sites & observed_sites).values()) + result["missingOccurrences"] = records(expected_sites - observed_sites) + result["unexpectedOccurrences"] = records(observed_sites - expected_sites) + result["matched"] = (result["relationshipMatched"] and not result["missingOccurrences"] + and not result["unexpectedOccurrences"]) + results.append(result) + return results + + +def execute(args: argparse.Namespace) -> None: + run_root = args.run.resolve() + code_files = [Path(__file__), Path(__file__).with_name("runner.py"), + Path(__file__).with_name("path_audit.py")] + code_digests = {path.name: _sha256_file(path) for path in code_files} + run_bytes = read_bounded(run_root / "run.json") + run = json.loads(run_bytes) + if run.get("schema") != "compass.agent-query-run/2": + raise ValueError("edge audit requires a provenance-recorded v2 run") + if _sha256_file(run_root / "suite.toml") != run["suiteDigest"]: + raise ValueError("captured suite digest mismatch") + manifest_bytes = read_bounded(args.witnesses) + manifest = json.loads(manifest_bytes) + if manifest.get("schema") != "compass.agent-edge-witnesses/1": + raise ValueError("unsupported edge witness schema") + if not 1 <= len(manifest["witnesses"]) <= 1000: + raise ValueError("witness count outside audit limit") + records = [record for record in run["repositories"] if record["repository"] == manifest["repository"]] + if len(records) != 1: + raise ValueError("expected one captured repository") + record = records[0] + repository = load_suite(run_root / "suite.toml").repository(manifest["repository"]) + if manifest["commit"] != repository.commit or record["commit"] != repository.commit: + raise ValueError("witness or captured source commit mismatch") + source = Path(record["source"]) + _verify_source(repository, source) + results = [] + for tool in ("compass", "graphify"): + graph_path = Path(record[f"{tool}Graph"]).resolve() + if not graph_path.is_relative_to(run_root): + raise ValueError("captured graph escapes the run directory") + graph_bytes = read_bounded(graph_path, MAX_GRAPH_BYTES) + if hashlib.sha256(graph_bytes).hexdigest() != record[f"{tool}GraphSha256"]: + raise ValueError("captured graph digest mismatch") + results.extend(audit_edges(manifest, tool, json.loads(graph_bytes), source)) + _verify_source(repository, source) + if code_digests != {path.name: _sha256_file(path) for path in code_files}: + raise ValueError("audit code changed during execution") + report = {"schema": "compass.agent-edge-audit/1", "runId": run["runId"], + "scope": manifest["scope"], "codeDigests": code_digests, + "runDigest": hashlib.sha256(run_bytes).hexdigest(), + "witnessDigest": hashlib.sha256(manifest_bytes).hexdigest(), "results": results} + args.output.parent.mkdir(parents=True, exist_ok=True) + with args.output.open("x", encoding="utf-8") as stream: + stream.write(json.dumps(report, indent=2, sort_keys=True) + "\n") + for row in results: + print(row["id"], row["tool"], "identity", row["endpointIdentityVerified"], + "relationship", row["relationshipMatched"], "all occurrences", row["matched"]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--run", type=Path, required=True) + parser.add_argument("--witnesses", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + execute(parser.parse_args()) diff --git a/benchmarks/agent_query/edge_witnesses_fd_diagnostic.json b/benchmarks/agent_query/edge_witnesses_fd_diagnostic.json new file mode 100644 index 000000000..10b785fa6 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_fd_diagnostic.json @@ -0,0 +1,127 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "fd", + "commit": "b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5", + "scope": "Post-output diagnostic added after the first fd run exposed an unresolved loop receiver and a wrong constructor owner. Kept separate from preregistered edge witnesses; not held-out accuracy or whole-graph precision/recall.", + "witnesses": [ + { + "id": "batch-builder-finish", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 191, + "symbol": "finish", + "text": "fn finish(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 111, + "text": "builder.finish()" + } + ], + "judgment": "Post-output source review of execute_batch: CommandBuilder::new at 97 constructs the values collected into builders; the successful match arm iterates them and calls the reviewed methods. No CommandTemplate::new call appears in this body." + }, + { + "id": "batch-builder-push", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 173, + "symbol": "push", + "text": "fn push(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 104, + "text": "builder.push(" + } + ], + "judgment": "Post-output source review of execute_batch: CommandBuilder::new at 97 constructs the values collected into builders; the successful match arm iterates them and calls the reviewed methods. No CommandTemplate::new call appears in this body." + }, + { + "id": "batch-builder-exit-code", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 205, + "symbol": "exit_code", + "text": "fn exit_code(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 116, + "text": "b.exit_code()" + } + ], + "judgment": "Post-output source review of execute_batch: CommandBuilder::new at 97 constructs the values collected into builders; the successful match arm iterates them and calls the reviewed methods. No CommandTemplate::new call appears in this body." + }, + { + "id": "batch-builder-new", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 136, + "symbol": "new", + "text": "fn new(template:" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/exec/mod.rs", + "line": 97, + "text": "CommandBuilder::new(c, limit)" + } + ], + "judgment": "Post-output source review of execute_batch: CommandBuilder::new at 97 constructs the values collected into builders; the successful match arm iterates them and calls the reviewed methods. No CommandTemplate::new call appears in this body." + }, + { + "id": "batch-not-template-new", + "source": { + "file": "src/exec/mod.rs", + "line": 90, + "symbol": "execute_batch", + "text": "pub fn execute_batch<" + }, + "target": { + "file": "src/exec/mod.rs", + "line": 220, + "symbol": "new", + "text": "fn new" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "Post-output source review of execute_batch: CommandBuilder::new at 97 constructs the values collected into builders; the successful match arm iterates them and calls the reviewed methods. No CommandTemplate::new call appears in this body." + } + ] +} diff --git a/benchmarks/agent_query/path_witnesses_fd.json b/benchmarks/agent_query/path_witnesses_fd.json new file mode 100644 index 000000000..3589e3cad --- /dev/null +++ b/benchmarks/agent_query/path_witnesses_fd.json @@ -0,0 +1,80 @@ +{ + "schema": "compass.agent-path-witnesses/1", + "scope": "Post-output development audit of two predeclared fd path questions. Checks printed direction, exact declaration identity and at least one compatible source occurrence in the captured graph; the rendered path does not identify a particular parallel edge or prove runtime execution. Not representative held-out accuracy.", + "witnesses": [ + { + "repository": "fd", + "commit": "b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5", + "question": "fd-path-command-error", + "category": "call", + "nodes": [ + { + "file": "src/exec/command.rs", + "line": 60, + "text": "pub fn execute_commands<", + "labels": [ + "execute_commands()" + ] + }, + { + "file": "src/exec/command.rs", + "line": 101, + "text": "pub fn handle_cmd_error(", + "labels": [ + "handle_cmd_error()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/exec/command.rs", + "line": 68, + "text": "handle_cmd_error(None, e)" + } + } + ] + }, + { + "repository": "fd", + "commit": "b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5", + "question": "fd-path-batch-exitcodes", + "category": "call", + "nodes": [ + { + "file": "src/exec/mod.rs", + "line": 90, + "text": "pub fn execute_batch<", + "labels": [ + ".execute_batch()" + ] + }, + { + "file": "src/exit_codes.rs", + "line": 46, + "text": "pub fn merge_exitcodes(", + "labels": [ + "merge_exitcodes()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/exec/mod.rs", + "line": 116, + "text": "merge_exitcodes(builders.iter()" + } + } + ] + } + ] +} diff --git a/benchmarks/agent_query/runner.py b/benchmarks/agent_query/runner.py index 23f6069df..f94600e99 100644 --- a/benchmarks/agent_query/runner.py +++ b/benchmarks/agent_query/runner.py @@ -912,7 +912,9 @@ def render_report(run: dict) -> str: lines.append(" not an independent precision oracle.") lines.append("- `graphify` prints an installation warning on stderr; the report counts stdout") lines.append(" only and records stderr separately in `run.json`.") - lines.append("- The suite is a focused sample of five repositories and does not estimate") + count = len(run["repositories"]) + noun = "repository" if count == 1 else "repositories" + lines.append(f"- The suite is a focused sample of {count} {noun} and does not estimate") lines.append(" population-wide accuracy.") lines.append("") return "\n".join(lines) diff --git a/benchmarks/agent_query/tests/test_edge_audit.py b/benchmarks/agent_query/tests/test_edge_audit.py new file mode 100644 index 000000000..7a7d34692 --- /dev/null +++ b/benchmarks/agent_query/tests/test_edge_audit.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import copy +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.edge_audit import audit_edges + + +class EdgeAuditTests(unittest.TestCase): + def setUp(self) -> None: + temporary = tempfile.TemporaryDirectory() + self.addCleanup(temporary.cleanup) + self.root = Path(temporary.name) + (self.root / "code.rs").write_text("fn caller() {}\nfn target() {}\ntarget();\ntarget();\n") + self.witness = {"id": "calls", "expected": "present", "relation": "calls", + "source": {"file": "code.rs", "line": 1, "symbol": "caller", "text": "fn caller"}, + "target": {"file": "code.rs", "line": 2, "symbol": "target", "text": "fn target"}, + "occurrences": [{"file": "code.rs", "line": line, "text": "target();"} + for line in (3, 4)]} + self.graph = {"nodes": [ + {"id": symbol, "name": symbol, "source": {"file": "code.rs", "startLine": line}} + for line, symbol in enumerate(("caller", "target"), 1)], + "edges": [{"source": "caller", "target": "target", "kind": "calls", + "relationshipSite": {"file": "code.rs", "startLine": line}} + for line in (3, 4)]} + + def audit(self, graph=None, tool="compass"): + return audit_edges({"witnesses": [self.witness]}, tool, + self.graph if graph is None else graph, self.root)[0] + + def test_complete_occurrences_match(self) -> None: + result = self.audit() + self.assertTrue(result["matched"]) + self.assertEqual(result["matchedOccurrences"], 2) + + def test_graphify_semantic_direction_matches_the_same_policy(self) -> None: + graph = {"nodes": [{"id": symbol, "label": symbol + "()", "source_file": "code.rs", + "source_location": f"L{line}"} + for line, symbol in enumerate(("caller", "target"), 1)], + "links": [{"source": "target", "target": "caller", "_src": "caller", "_tgt": "target", + "relation": "calls", "source_file": "code.rs", "source_location": f"L{line}"} + for line in (3, 4)]} + self.assertTrue(self.audit(graph, "graphify")["matched"]) + + def test_one_edge_does_not_prove_all_occurrences(self) -> None: + self.graph["edges"].pop() + result = self.audit() + self.assertTrue(result["relationshipMatched"]) + self.assertFalse(result["matched"]) + self.assertEqual(result["matchedOccurrences"], 1) + self.assertEqual(result["missingOccurrences"], [{"file": "code.rs", "line": 4, "count": 1}]) + + def test_duplicate_site_does_not_recover_a_missing_site(self) -> None: + self.graph["edges"][1] = copy.deepcopy(self.graph["edges"][0]) + result = self.audit() + self.assertFalse(result["matched"]) + self.assertEqual(result["matchedOccurrences"], 1) + self.assertEqual(result["unexpectedOccurrences"], [{"file": "code.rs", "line": 3, "count": 1}]) + + def test_negative_requires_both_endpoint_identities(self) -> None: + self.witness.update(expected="absent", occurrences=[]) + self.assertFalse(self.audit()["matched"]) + self.graph["edges"] = [] + self.assertTrue(self.audit()["matched"]) + self.graph["nodes"].pop() + self.assertFalse(self.audit()["matched"]) + + def test_wrong_owner_or_direction_never_matches(self) -> None: + for reverse in (False, True): + graph = copy.deepcopy(self.graph) + other = copy.deepcopy(graph["nodes"][1]) + other["id"] = "other-owner" + other["source"]["startLine"] = 99 + other["source"]["file"] = "elsewhere.rs" + graph["nodes"].append(other) + for edge in graph["edges"]: + if reverse: + edge["source"], edge["target"] = edge["target"], edge["source"] + else: + edge["target"] = "other-owner" + self.assertFalse(self.audit(graph)["relationshipMatched"]) + + def test_ambiguous_anchors_and_invalid_ids_cannot_pass(self) -> None: + rival = copy.deepcopy(self.graph["nodes"][0]) + rival["id"] = "another-caller" + self.graph["nodes"].append(rival) + result = self.audit() + self.assertFalse(result["endpointIdentityVerified"]) + self.assertEqual(result["expectedOccurrences"], 2) + rival["id"] = "caller" + with self.assertRaisesRegex(ValueError, "unique nonempty"): + self.audit() + + def test_source_drift_or_malformed_expectation_fails_before_scoring(self) -> None: + self.witness["expected"] = "maybe" + with self.assertRaisesRegex(ValueError, "unsupported edge expectation"): + self.audit() + self.witness["expected"] = "present" + (self.root / "code.rs").write_text("changed\n") + with self.assertRaisesRegex(ValueError, "source witness changed"): + self.audit() + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmarks/agent_query/tests/test_runner.py b/benchmarks/agent_query/tests/test_runner.py index e82d700d2..abf248e27 100644 --- a/benchmarks/agent_query/tests/test_runner.py +++ b/benchmarks/agent_query/tests/test_runner.py @@ -23,6 +23,7 @@ graph_metrics, judge, load_suite, + render_report, ) ROOT = Path(__file__).resolve().parents[1] @@ -50,6 +51,38 @@ def question(**overrides) -> Question: class SuiteTests(unittest.TestCase): + def test_fd_source_first_inputs_remain_distinct_and_pinned(self) -> None: + suite = load_suite(ROOT / "suite_fd.toml") + self.assertEqual(len(suite.repositories), 1) + repository = suite.repository("fd") + self.assertEqual(len(repository.questions), 12) + self.assertEqual(len(repository.anchors), 5) + manifest = json.loads((ROOT / "edge_witnesses_fd.json").read_text()) + self.assertEqual(manifest["schema"], "compass.agent-edge-witnesses/1") + self.assertEqual(manifest["repository"], repository.name) + self.assertEqual(manifest["commit"], repository.commit) + witnesses = manifest["witnesses"] + self.assertEqual(len({row["id"] for row in witnesses}), 12) + self.assertEqual(sum(row["expected"] == "present" for row in witnesses), 10) + self.assertEqual(sum(len(row["occurrences"]) for row in witnesses), 16) + for row in witnesses: + self.assertEqual(bool(row["occurrences"]), row["expected"] == "present") + self.assertTrue(row["judgment"]) + for question in repository.questions: + self.assertNotIn("--brief", question.compass) + if question.kind in {"path", "file_path"}: + self.assertIn("--undirected", question.graphify) + + def test_report_describes_the_actual_repository_count(self) -> None: + run = {"runId": "test", "suiteDigest": "digest", "startedAt": "date", + "tools": [], "graphMetrics": [], "observations": [], "questions": [], + "summaries": {}, "paired": {}, "repositories": [{"repository": "fd"}], + "verdict": {key: "unmeasured" for key in + ("correctness", "tokens", "pairedTokens", "split", "graphQuality")}} + self.assertIn("sample of 1 repository", render_report(run)) + run["repositories"].append({"repository": "second"}) + self.assertIn("sample of 2 repositories", render_report(run)) + def test_checked_in_suite_covers_five_languages(self) -> None: suite = load_suite(ROOT / "suite.toml") self.assertEqual(len(suite.digest), 64) diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 8e7fe34c3..f7d7692f6 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -9,9 +9,9 @@ finding. A focused text-recall score cannot establish all of those properties. | Requirement | Evidence needed | Current evidence | | --- | --- | --- | | Reliable hub analysis | Declaration-aware candidates, stable rankings, source-reviewed false positives and negatives | Five hub defects fixed; no reviewed god-object corpus yet | -| Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Anchor scorer repaired; relationship accuracy not measured by that scorer | -| Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Existing five-repository suites are development samples with text oracles | -| Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Prior query changes exist; fresh paired evidence still needed | +| Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Source-first fd pair/occurrence audit added; loop recall and shadowed-receiver precision defects remain open | +| Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Five-repository development suites plus a separately selected source-first fd sample; neither establishes representative accuracy | +| Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Fresh paired fd answers expose a Compass callees miss and a Graphify wrong-owner edge hidden by the text oracle | | Better navigation and walks | Valid ordered edges, direction, hop bounds, alternatives, ambiguity and negative cases | Existing path tests/suites are useful but do not prove real-repository path precision | | Fair efficiency comparison | Same successful questions, repeated timings, token methodology and complete environment provenance | Paired token aggregation exists; bytes/4 remains an estimate | @@ -292,9 +292,105 @@ Graphify exposes no documented hop-bound option. A one-hop request correctly returns a bounded no-path result; the two-hop request reproduced the third navigation defect above. These are not extra head-to-head accuracy wins. +## Separate source-first fd sample + +The inputs in `benchmarks/agent_query/suite_fd.toml` and +`edge_witnesses_fd.json` were committed at `fe1a1fb5` before either tool was +run on this checkout. Source selection used `sharkdp/fd` at +`b422e5d8c9cffaa1ae43ba68e7b97a60fb3e8ae5`, with a separate clean working +checkout because the existing repository was bare. The sources stayed pinned +and clean before/after extraction. This is a source-first selected sample, +not a representative held-out corpus. It becomes development evidence once +used to guide fixes. + +The fresh run `fd-source-first-01` uses the frozen debug Compass executable +with all eight preceding fixes. It records both graph digests, executable +identities, build logs and every query response. It is not performance evidence. + +| Measured item | Compass | Graphify 0.9.67 | +| --- | ---: | ---: | +| Predeclared text-oracle passes | 11/12 | 9/12 | +| Non-excerpt passes | 10/11 | 9/11 | +| Exact reviewed declaration anchors | 5/5 | 5/5 | +| Predeclared positive direct-call pairs present | 10/10 | 10/10 | +| Reviewed line-level call occurrences preserved | 16/16 | 10/16 | +| Predeclared wrong-target pairs correctly absent | 2/2 | 2/2 | +| Post-output positive path witnesses | 2/2 | 2/2 | +| Median estimated tokens on eight shared passes | 227 | 88 | + +Relationship presence and occurrence preservation are different measurements. +Graphify retains one occurrence for each reviewed pair, losing six repeated +sites across four pairs. These are selected-pair results, not whole-graph +precision or recall. The positive path review confirms an adjacent directed +call and a compatible source occurrence in each graph; neither rendered path +names a particular parallel edge or proves runtime execution. + +The Compass failure is real: the `execute_batch` callees answer omits +`CommandBuilder::finish` at `src/exec/mod.rs:111`. The graph also lacks that +method's calls to `CommandBuilder::push` at line 104 and `exit_code` at line +116. The enclosing function maps constructors into a collected result, +destructures its success case, then iterates the builders. Graphify finds all +three method targets. Compass's Rust value-type collector handles function +parameters and local lets, but does not establish the loop/match/iterator +binding chain needed here. This is an extraction/resolution gap, not an +answer-rendering error. + +The same answer exposes a Graphify precision error: its call at line 97 targets +`CommandTemplate::new` (declaration line 220), although the source explicitly +calls `CommandBuilder::new` (declaration line 136). Compass targets the correct +constructor. The post-output manifest `edge_witnesses_fd_diagnostic.json` +records those five checks separately. Graphify's text-oracle pass is retained; +it does not prove every returned edge correct. + +Graphify's other text failures are the source-excerpt feature gap, a file +lookup resolving `src/exec/mod.rs` to the `Exec` declaration in +`src/cli.rs:858`, and a broad question that never returns `handle_cmd_error` +within its three budget increases. Compass needs two follow-up pages for +that broad question (1,769 estimated total tokens), so its pass also identifies +room to improve ranking and answer size. The lower paired token median remains +a Graphify advantage. + +The first generated report incorrectly describes every suite as containing +five repositories. Its original report is retained. The renderer now uses the +recorded repository count, with singular/plural regression coverage; this +wording correction changes no scores or raw observations. + +### Open Rust receiver correctness defect + +Reducing the loop miss uncovered false-positive calls in Compass. The frozen +eight-fix executable emits `Decoy::finish` for both calls below, although the +loop call is on `Actual`: + +```rust +struct Actual; +impl Actual { fn finish(&self) {} } +struct Decoy; +impl Decoy { fn finish(&self) {} } +fn run(builder: &Decoy, builders: &[Actual]) { + for builder in builders { builder.finish(); } + builder.finish(); +} +``` + +A nested `let builder = factory();` also inherits the outer parameter's +type when the local initializer does not yield a producer-known type. +Unknown local types currently fall through the value-type lookup, while the +parameter's alias remains available to call resolution. These are open +precision defects, not acceptable substitutes for unresolved evidence. +Retained reductions are under `rust-loop-diagnostic-01` and +`rust-shadow-diagnostic-02`; the latter uses a source-defined factory returning +a different declared type. Scope blocking and native negative regressions +must precede broader iterator inference. + +The new edge auditor and report/input regression coverage pass 48 Python +tests. No production Rust change is included in this evaluation checkpoint; +the prior native verification ledger still describes the eight-fix executable. + ## Next evidence to collect -1. Keep exact build/source provenance for subsequent release comparisons; +1. Fix and qualify Rust receiver shadowing before extending source-proven + loop/result/iterator inference to recover the fd callees miss. Keep exact + build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. 2. Expand hub review beyond candidate eligibility to source-reviewed design judgments, separating connectivity from responsibility/cohesion defects. From b62534cf092057f8dad951b5fe3fd36e859b2c68 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 15:17:41 -0700 Subject: [PATCH 08/97] fix: preserve Rust receiver shadowing and binding lifetimes --- CHANGELOG.md | 4 + COMPATIBILITY.md | 9 + MIGRATION.md | 8 + crates/compass-files/src/cache.rs | 2 +- crates/compass-files/tests/contracts.rs | 10 +- .../compass-languages/src/evidence/build.rs | 206 +++++++++++++++--- .../tests/universal_resolution/rust.rs | 100 +++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 40 +++- docs/reference/universal-semantic-evidence.md | 9 + 9 files changed, 348 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d40680af..a46cd5692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Prevent Rust local receiver bindings from inheriting shadowed outer parameter + types in lets, loops, closures, match arms/guards, and conditional lets. + Preserve initializer and else-branch scope, and rebuild older AST caches. + - Fix weighted `path --max-depth` searches losing a feasible shorter prefix. Search work exhaustion now fails explicitly instead of appearing disconnected. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 96e8cdae2..d77e57efc 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -133,6 +133,15 @@ evaluating local macro inputs can publish newly recovered exact calls. Unsupported macro shapes, non-evaluating inputs, and ambiguous receiver owners remain unresolved rather than being guessed. +Rust receiver lookup now respects local shadowing in lets, loops, closures, +match arms/guards, and conditional lets. An unknown inner type cannot inherit +an outer parameter's alias. A let initializer still sees the previous binding; +an `else` branch does not see bindings from a failed condition. Previously +incorrect call edges can disappear and remain unresolved until the receiver +type is proven. The advertised producer capabilities and evidence/graph schemas +are unchanged. AST cache semantics advance from 2 to 3, rebuilding prior AST +facts automatically across languages. Published historical graphs are unchanged. + ### Agent Query View Compass adds the additive strict projection `compass.query.agent-view/1` for diff --git a/MIGRATION.md b/MIGRATION.md index 1619fe40a..5d75d8f6d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -42,6 +42,14 @@ accept the shared `--format text|json|agent-json` contract where applicable. Use `compass architecture --format agent-json` for a bounded repository overview with omission counts and witness IDs. +## Rebuild cached AST facts after receiver corrections + +AST cache semantics version 3 invalidates older cached extractions, including +Rust calls incorrectly attributed to an outer variable shadowed by a local +binding. The next extraction rebuilds these facts automatically and can take +longer. Re-extract existing graphs to receive the correction; historical +realizations remain immutable. No source or configuration migration is needed. + ## Rebuild SQLite adjacency sidecars Store snapshots now declare edge-ID-ordered directional adjacency so bounded diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index 0cc444222..fbf975db9 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "2"; +pub const AST_CACHE_VERSION: &str = "3"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index 8a58dc5eb..caac1633a 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -821,7 +821,7 @@ fn build_guard_publishes_one_complete_snapshot_at_a_time() -> Result<(), Box Result<(), Box> { let directory = tempfile::tempdir()?; let guard = BuildGuard::begin(directory.path())?; - write_text_atomic(&guard.staging_directory().join("graph.json"), "graph")?; + write_text_atomic(guard.staging_directory().join("graph.json"), "graph")?; guard.commit_with_presealed_artifacts(&["graph.json"])?; assert_eq!( fs::read_to_string(BuildGuard::resolve_artifact( @@ -985,6 +985,13 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< cache_root.join("compass-out/cache/ast/v0.9.21/stale.json"), "{}", )?; + // Version 2 can contain calls attributed to a shadowed outer Rust + // receiver. Those facts must not survive the semantics correction. + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v2/e1"))?; + fs::write( + cache_root.join("compass-out/cache/ast/v2/e1/stale.msgpack"), + "stale", + )?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/vold"))?; fs::write( cache_root.join("compass-out/cache/ast/vold/stale.json"), @@ -1012,6 +1019,7 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< )) ); assert!(!cache_root.join("compass-out/cache/ast/v0.9.21").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v2").exists()); let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index 8094ba4e5..b55b94ce8 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -972,7 +972,8 @@ struct RustImplContext { #[derive(Clone)] struct RustValueTypeVersion { raw: Option, - active_from: usize, + active_range: std::ops::Range, + shadows_alias: bool, } #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -4847,7 +4848,8 @@ impl<'source> DirectEvidenceState<'source> { scope_id: &str, name: &str, raw: Option, - active_from: usize, + active_range: std::ops::Range, + shadows_alias: bool, ) { if name.is_empty() || name == "_" || name == "self" { return; @@ -4857,7 +4859,11 @@ impl<'source> DirectEvidenceState<'source> { .or_default() .entry(name.to_owned()) .or_default() - .push(RustValueTypeVersion { raw, active_from }); + .push(RustValueTypeVersion { + raw, + active_range, + shadows_alias, + }); } fn rust_value_type_for<'a>( @@ -4867,12 +4873,23 @@ impl<'source> DirectEvidenceState<'source> { use_start: usize, use_node: Option>, ) -> Option<&'a str> { + self.rust_value_binding_for(owner, name, use_start, use_node) + .and_then(|version| version.raw.as_deref()) + } + + fn rust_value_binding_for<'a>( + &'a self, + owner: &DeclarationContext, + name: &str, + use_start: usize, + use_node: Option>, + ) -> Option<&'a RustValueTypeVersion> { let mut node = use_node; while let Some(current) = node { if rust_is_lexical_scope_node(current.kind()) { let scope_id = format!("rust-lexical:{}", current.id()); - if let Some(raw) = self.rust_value_type_in_scope(&scope_id, name, use_start) { - return Some(raw); + if let Some(version) = self.rust_value_type_in_scope(&scope_id, name, use_start) { + return Some(version); } } node = current.parent(); @@ -4880,8 +4897,8 @@ impl<'source> DirectEvidenceState<'source> { let mut scope_id = Some(owner.scope_id.as_str()); for _ in 0..64 { let current = scope_id?; - if let Some(raw) = self.rust_value_type_in_scope(current, name, use_start) { - return Some(raw); + if let Some(version) = self.rust_value_type_in_scope(current, name, use_start) { + return Some(version); } scope_id = self.scope_parents.get(current).map(String::as_str); } @@ -4893,17 +4910,16 @@ impl<'source> DirectEvidenceState<'source> { scope_id: &str, name: &str, use_start: usize, - ) -> Option<&'a str> { + ) -> Option<&'a RustValueTypeVersion> { self.rust_value_types .get(scope_id) .and_then(|values| values.get(name)) .and_then(|versions| { versions .iter() - .filter(|version| version.active_from <= use_start) - .max_by_key(|version| version.active_from) + .filter(|version| version.active_range.contains(&use_start)) + .max_by_key(|version| version.active_range.start) }) - .and_then(|version| version.raw.as_deref()) } fn ensure_rust_lexical_scope(&mut self, node: Node<'_>, parent: &str) -> String { @@ -4991,22 +5007,34 @@ impl<'source> DirectEvidenceState<'source> { None } - fn collect_rust_parameter_value_types(&mut self, parameters: Node<'_>, scope_id: &str) { + fn collect_rust_parameter_value_types( + &mut self, + parameters: Node<'_>, + scope_id: &str, + shadows_alias: bool, + ) { let mut cursor = parameters.walk(); for parameter in parameters .children(&mut cursor) .filter(|child| child.is_named()) { - let Some(pattern) = parameter.child_by_field_name("pattern") else { - continue; - }; + let pattern = parameter + .child_by_field_name("pattern") + .unwrap_or(parameter); let raw = parameter .child_by_field_name("type") + .filter(|_| rust_pattern_binds_whole_value(pattern)) .map(|type_node| self.text(type_node)); let mut names = Vec::new(); collect_rust_pattern_names(pattern, &mut names, self.source); for name in names { - self.record_rust_value_type(scope_id, &name, raw.clone(), parameter.start_byte()); + self.record_rust_value_type( + scope_id, + &name, + raw.clone(), + parameter.start_byte()..usize::MAX, + shadows_alias, + ); } } } @@ -5014,7 +5042,7 @@ impl<'source> DirectEvidenceState<'source> { fn collect_rust_value_types(&mut self, callable: Node<'_>, owner: &DeclarationContext) { let scope_id = owner.scope_id.clone(); if let Some(parameters) = callable.child_by_field_name("parameters") { - self.collect_rust_parameter_value_types(parameters, &scope_id); + self.collect_rust_parameter_value_types(parameters, &scope_id, false); } let Some(body) = callable.child_by_field_name("body") else { return; @@ -5040,22 +5068,87 @@ impl<'source> DirectEvidenceState<'source> { if node.kind() == "closure_expression" && let Some(parameters) = node.child_by_field_name("parameters") { - self.collect_rust_parameter_value_types(parameters, &scope_id); + self.collect_rust_parameter_value_types(parameters, &scope_id, true); } if node.kind() == "let_declaration" && let Some(pattern) = node.child_by_field_name("pattern") { - let raw = node - .child_by_field_name("type") - .map(|type_node| self.text(type_node)) - .or_else(|| { - node.child_by_field_name("value") - .and_then(|value| self.rust_inferred_value_type(value, owner)) - }); + let raw = rust_pattern_binds_whole_value(pattern) + .then(|| { + node.child_by_field_name("type") + .map(|type_node| self.text(type_node)) + .or_else(|| { + node.child_by_field_name("value") + .and_then(|value| self.rust_inferred_value_type(value, owner)) + }) + }) + .flatten(); let mut names = Vec::new(); collect_rust_pattern_names(pattern, &mut names, self.source); for name in names { - self.record_rust_value_type(&scope_id, &name, raw.clone(), pattern.start_byte()); + // A let binding starts after its initializer; the previous + // binding remains visible while the initializer is evaluated. + self.record_rust_value_type( + &scope_id, + &name, + raw.clone(), + node.end_byte()..usize::MAX, + true, + ); + } + } + if matches!(node.kind(), "for_expression" | "match_arm") + && let Some(pattern) = node.child_by_field_name("pattern") + { + let active_from = if node.kind() == "for_expression" { + node.child_by_field_name("body") + .map(|body| body.start_byte()) + } else { + pattern.named_child(0).map(|pattern| pattern.end_byte()) + }; + if let Some(active_from) = active_from { + let mut names = Vec::new(); + collect_rust_pattern_names(pattern, &mut names, self.source); + for name in names { + // An unknown inner receiver still shadows an outer alias. + // Do not invent an element type from a method's spelling. + self.record_rust_value_type( + &scope_id, + &name, + None, + active_from..node.end_byte(), + true, + ); + } + } + } + if node.kind() == "let_condition" + && let Some(pattern) = node.child_by_field_name("pattern") + { + let mut enclosing = node.parent(); + while let Some(conditional) = enclosing { + if matches!(conditional.kind(), "if_expression" | "while_expression") { + let body = conditional + .child_by_field_name("consequence") + .or_else(|| conditional.child_by_field_name("body")); + if let Some(body) = body { + let mut names = Vec::new(); + collect_rust_pattern_names(pattern, &mut names, self.source); + for name in names { + // Let-chain guards and the success body see this + // binding; its initializer and else branch do not. + self.record_rust_value_type( + &scope_id, + &name, + None, + node.end_byte()..body.end_byte(), + true, + ); + } + } + break; + } + enclosing = conditional.parent(); } } let mut cursor = node.walk(); @@ -5600,8 +5693,11 @@ impl<'source> DirectEvidenceState<'source> { if import_binding_is_ambiguous && platform_reexport_bindings.is_none() { return Ok(()); } - let direct_binding = platform_reexport_bindings - .is_none() + let shadows_alias = function.kind() == "field_expression" + && self + .rust_value_binding_for(owner, binding_name, function.start_byte(), Some(function)) + .is_some_and(|version| version.shadows_alias); + let direct_binding = (platform_reexport_bindings.is_none() && !shadows_alias) .then(|| { if uses_type_namespace { self.import_binding_version_at(owner, binding_name, function.start_byte(), true) @@ -5832,6 +5928,20 @@ impl<'source> DirectEvidenceState<'source> { { return Some(rust_join_qualified(&receiver_type, spelling)); } + if use_node.kind() == "field_expression" + && self + .rust_value_binding_for( + owner, + qualified_binding_head(qualifier), + use_start, + Some(use_node), + ) + .is_some_and(|version| version.shadows_alias) + { + // A local with no proven receiver type must not fall back to a + // same-named parameter/import binding from the enclosing scope. + return None; + } if let Some(target) = self.local_target_for(owner, qualifier) { if let Some(method) = self.rust_receiver_method_target(target, spelling) { return Some(method); @@ -11518,7 +11628,7 @@ fn qualified_binding_head(qualifier: &str) -> &str { } fn collect_rust_pattern_names(node: Node<'_>, names: &mut Vec, source: &[u8]) { - if node.kind() == "identifier" { + if matches!(node.kind(), "identifier" | "shorthand_field_identifier") { let name = source .get(node.start_byte()..node.end_byte()) .map_or_else(String::new, |bytes| { @@ -11530,15 +11640,49 @@ fn collect_rust_pattern_names(node: Node<'_>, names: &mut Vec, source: & return; } let mut cursor = node.walk(); - for child in node.children(&mut cursor).filter(|child| child.is_named()) { + for (index, child) in node + .children(&mut cursor) + .enumerate() + .filter(|(_, child)| child.is_named()) + { + if matches!( + node.field_name_for_child(index as u32), + Some("type" | "condition") + ) { + continue; + } collect_rust_pattern_names(child, names, source); } } +fn rust_pattern_binds_whole_value(mut node: Node<'_>) -> bool { + for _ in 0..16 { + match node.kind() { + "identifier" => return true, + "mut_pattern" | "ref_pattern" | "reference_pattern" => { + let mut cursor = node.walk(); + let Some(child) = node.named_children(&mut cursor).last() else { + return false; + }; + node = child; + } + _ => return false, + } + } + false +} + fn rust_is_lexical_scope_node(kind: &str) -> bool { matches!( kind, - "block" | "match_block" | "unsafe_block" | "closure_expression" + "block" + | "match_block" + | "unsafe_block" + | "closure_expression" + | "for_expression" + | "match_arm" + | "if_expression" + | "while_expression" ) } diff --git a/crates/compass-resolve/tests/universal_resolution/rust.rs b/crates/compass-resolve/tests/universal_resolution/rust.rs index 180a75df4..387d0af35 100644 --- a/crates/compass-resolve/tests/universal_resolution/rust.rs +++ b/crates/compass-resolve/tests/universal_resolution/rust.rs @@ -3123,3 +3123,103 @@ fn rust_wildcard_bindings_resolve_local_exports_and_preserve_external_candidates && edge.string("resolution_rule") == "qualified-external" })); } + +#[test] +fn rust_shadowed_receivers_never_capture_an_outer_parameter() { + for (case, body) in [ + ("unknown let", "{ let builder = factory(); builder.finish(); }"), + ("known let", "{ let builder: Actual = factory(); builder.finish(); }"), + ("loop", "for builder in builders { builder.finish(); }"), + ("closure", "builders.iter().for_each(|builder| builder.finish());"), + ("match arm", "match builders.first() { Some(builder) => builder.finish(), None => {} }"), + ("match guard", "match builders.first() { Some(builder) if { builder.finish(); true } => builder.finish(), _ => {} }"), + ("if let", "if let Some(builder) = builders.first() { builder.finish(); }"), + ("while let", "while let Some(builder) = builders.first() { builder.finish(); break; }"), + ("let chain", "if let Some(builder) = builders.first() && { builder.finish(); true } { builder.finish(); }"), + ("destructured let", "struct Wrap { builder: Actual } impl Wrap { fn finish(&self) {} } { let Wrap { builder } = Wrap { builder: factory() }; builder.finish(); }"), + ] { + let source = format!( + "struct Actual;\nimpl Actual {{ fn finish(&self) {{}} }}\n\ + struct Decoy;\nimpl Decoy {{ fn finish(&self) {{}} }}\n\ + fn factory() -> Actual {{ Actual }}\n\ + fn run(builder: &Decoy, builders: &[Actual]) {{\n {body}\n builder.finish();\n}}\n" + ); + let extracted = extract("src/lib.rs", source.as_bytes()); + let evidence = extracted.semantic_evidence.as_ref().expect("Rust evidence"); + assert_eq!(evidence.occurrences.iter().filter(|occurrence| { + occurrence.role == compass_languages::SemanticRole::Call + && occurrence.spelling == "finish" + }).count(), source.matches(".finish()").count(), "{case}: all source occurrences must remain visible"); + let resolved = compass_resolve::resolve( + &[extracted], &HashMap::from([("src/lib.rs".to_owned(), source)]), + ); + let run = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::run") + .expect("run declaration"); + let decoy = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::Decoy::finish") + .expect("Decoy method"); + let calls = resolved.edges.iter().filter(|edge| { + edge.source == run.id && edge.target == decoy.id && edge.string("relation") == "calls" + }).collect::>(); + assert_eq!(calls.len(), 1, "{case}: only the unshadowed outer call may target Decoy: {calls:#?}"); + assert_eq!(calls[0].string("source_location"), "L8", "{case}"); + assert_eq!(calls[0].string("confidence"), "EXTRACTED", "{case}"); + let actual = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::Actual::finish") + .expect("Actual method"); + for edge in resolved.edges.iter().filter(|edge| edge.source == run.id + && edge.string("relation") == "calls" && edge.string("source_location") == "L7") { + let target = resolved.nodes.iter().find(|node| node.id == edge.target).expect("call target"); + if target.string("qualified_name").ends_with("::finish") + && target.attributes.get("placeholder").and_then(serde_json::Value::as_bool) != Some(true) { + assert_eq!(target.id, actual.id, "{case}: inner call must not target the container or outer receiver"); + } + } + if case == "known let" { + assert!(resolved.edges.iter().any(|edge| edge.source == run.id + && edge.target == actual.id && edge.string("relation") == "calls" + && edge.string("source_location") == "L7"), "typed inner call must still resolve"); + } + } +} + +#[test] +fn rust_let_initializer_uses_the_previous_receiver_binding() { + let source = b"struct Actual; +impl Actual { fn finish(&self) {} } +struct Decoy; +impl Decoy { fn replace(&self) -> Actual { Actual } } +fn run(builder: &Decoy) { + let builder: Actual = builder.replace(); + builder.finish(); +} + +"; + let resolved = compass_resolve::resolve(&[extract("src/lib.rs", source)], + &HashMap::from([("src/lib.rs".to_owned(), String::from_utf8(source.to_vec()).expect("source"))])); + for (target, site) in [("crate::Decoy::replace", "L6"), ("crate::Actual::finish", "L7")] { + let target = resolved.nodes.iter().find(|node| node.string("qualified_name") == target).expect("target"); + assert!(resolved.edges.iter().any(|edge| edge.target == target.id + && edge.string("relation") == "calls" && edge.string("source_location") == site), + "missing {site} -> {}", target.string("qualified_name")); + } +} + +#[test] +fn rust_condition_binding_is_not_visible_in_else_or_afterward() { + let source = b"struct Actual; +impl Actual { fn finish(&self) {} } +struct Decoy; +impl Decoy { fn finish(&self) {} } +fn run(builder: &Decoy, builders: &[Actual]) { + if let Some(builder) = builders.first() { builder.finish(); } + else { builder.finish(); } + builder.finish(); +} +"; + let resolved = compass_resolve::resolve(&[extract("src/lib.rs", source)], + &HashMap::from([("src/lib.rs".to_owned(), String::from_utf8(source.to_vec()).expect("source"))])); + let decoy = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::Decoy::finish") + .expect("Decoy method"); + let sites = resolved.edges.iter().filter(|edge| edge.target == decoy.id && edge.string("relation") == "calls") + .map(|edge| edge.string("source_location")).collect::>(); + assert_eq!(sites, BTreeSet::from(["L7".to_owned(), "L8".to_owned()])); +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index f7d7692f6..cd4c54a50 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -9,7 +9,7 @@ finding. A focused text-recall score cannot establish all of those properties. | Requirement | Evidence needed | Current evidence | | --- | --- | --- | | Reliable hub analysis | Declaration-aware candidates, stable rankings, source-reviewed false positives and negatives | Five hub defects fixed; no reviewed god-object corpus yet | -| Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Source-first fd pair/occurrence audit added; loop recall and shadowed-receiver precision defects remain open | +| Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Source-first fd pair/occurrence audit added; receiver-shadowing correction has native regressions; fd loop recall remains open | | Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Five-repository development suites plus a separately selected source-first fd sample; neither establishes representative accuracy | | Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Fresh paired fd answers expose a Compass callees miss and a Graphify wrong-owner edge hidden by the text oracle | | Better navigation and walks | Valid ordered edges, direction, hop bounds, alternatives, ambiguity and negative cases | Existing path tests/suites are useful but do not prove real-repository path precision | @@ -355,7 +355,7 @@ five repositories. Its original report is retained. The renderer now uses the recorded repository count, with singular/plural regression coverage; this wording correction changes no scores or raw observations. -### Open Rust receiver correctness defect +### Rust receiver correctness defect and correction Reducing the loop miss uncovered false-positive calls in Compass. The frozen eight-fix executable emits `Decoy::finish` for both calls below, although the @@ -375,21 +375,47 @@ fn run(builder: &Decoy, builders: &[Actual]) { A nested `let builder = factory();` also inherits the outer parameter's type when the local initializer does not yield a producer-known type. Unknown local types currently fall through the value-type lookup, while the -parameter's alias remains available to call resolution. These are open +parameter's alias remains available to call resolution. These are precision defects, not acceptable substitutes for unresolved evidence. Retained reductions are under `rust-loop-diagnostic-01` and `rust-shadow-diagnostic-02`; the latter uses a source-defined factory returning -a different declared type. Scope blocking and native negative regressions -must precede broader iterator inference. +a different declared type. The new edge auditor and report/input regression coverage pass 48 Python tests. No production Rust change is included in this evaluation checkpoint; the prior native verification ledger still describes the eight-fix executable. +The subsequent correction is in the Rust evidence producer. Value lookup now +distinguishes an absent binding from an existing binding of unknown type. +Local bindings suppress stale parameter aliases, including loop and closure +patterns, match arms/guards, conditional lets, and let-chain guards. Binding +visibility is bounded by lexical scope and source range; initializers and else +branches keep their proper outer bindings. Destructuring records the bound +names without assigning the whole container's type to each field. If an inner +receiver's type remains unknown, its source occurrence and unresolved candidate +remain visible; no target is invented. + +A native regression failed before the fix by returning two calls to `Decoy` +where only the unshadowed outer call is valid. The expanded regression covers +ten scope/pattern forms, initializer timing, preserved typed inner calls, and +else-branch visibility. The current targeted checks pass 27 Rust language +tests, all 206 universal resolver integration tests, and 33 filesystem +contracts. Workspace and changed integration-test Clippy pass with warnings +denied. An existing needless borrow in the touched filesystem test was also +removed to allow that test target's Clippy check. + +AST cache semantics advance from 2 to 3 so warm builds cannot keep previously +incorrect relationships. A cache regression checks that version-2 facts are +discarded. This is a correction to the existing producer contract, not a new +advertised capability or a new universal-pipeline promotion. Evidence/graph +schema majors and the Rust producer capability identity remain unchanged; +historical graphs are not rewritten. Full baseline, fixture, and fresh binary +replay evidence for this correction is recorded below as it completes. + ## Next evidence to collect -1. Fix and qualify Rust receiver shadowing before extending source-proven - loop/result/iterator inference to recover the fd callees miss. Keep exact +1. Complete qualification and fresh binary replay of receiver shadowing, then + extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. 2. Expand hub review beyond candidate eligibility to source-reviewed design diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index cd23aaa2a..5cf2b7c9a 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -193,6 +193,15 @@ proves that ownership. Unshadowed `Option` and `Result` use their canonical standard-library identities; other unproven spellings remain unresolved rather than becoming crate-qualified placeholders. +Rust value lookup preserves unknown local bindings as shadowing boundaries. +Loop patterns, closure parameters, match patterns/guards, and conditional-let +bindings cannot reuse an outer parameter alias. Let initializers retain the +previous binding, and condition bindings end before an else branch. An unknown +inner receiver retains its source occurrence and unresolved candidate instead +of acquiring an invented target. This correctness correction keeps the existing +producer capability contract; AST cache semantics version 3 prevents reuse of +facts produced before it. + Rust producer version 2 follows fields through source-proven standard-library `Arc`, `Rc`, and `Box` dereference wrappers and carries a unique source-visible call-result type into the next member call. It also inspects a local From 86958dfa62e00b976558f771fba18be739ab8c2f Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 15:27:30 -0700 Subject: [PATCH 09/97] test: preregister natural-language tasks across five languages --- benchmarks/agent_query/COVERAGE_PLAN.md | 79 +++++++ benchmarks/agent_query/README.md | 13 +- benchmarks/agent_query/suite_ask.toml | 227 ++++++++++++++++++++ benchmarks/agent_query/tests/test_runner.py | 13 ++ 4 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 benchmarks/agent_query/COVERAGE_PLAN.md create mode 100644 benchmarks/agent_query/suite_ask.toml diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md new file mode 100644 index 000000000..56affecbe --- /dev/null +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -0,0 +1,79 @@ +# Real-repository comparison coverage + +This plan extends the existing paired runs to the user's requested query, +ask, node, path, caller/callee, cluster/community, and god-node surfaces. +It is a work plan, not a completed evaluation or a superiority claim. + +## Repository panel + +Keep the same clean, pinned source on both sides. The current panel includes +Cobra (Go), Flask (Python), Gson (Java), Zod (TypeScript), and Axum (Rust), +plus the separate fd (Rust) source-first sample. Their commits and source roots +are recorded in `suite_v2.toml`, `suite_fd.toml`, and each captured run. +Use additional independently selected repositories for confirmation after +improving on these development cases. Do not relabel used questions as held out. + +## Question and evidence matrix + +Each new question must record its exact source witness, expected outcome, +per-tool operation, bounds, and judgment before executing that question. +No output-derived oracle correction may silently replace an earlier result. + +| Surface | Questions on every language | Primary correctness evidence | +| --- | --- | --- | +| Node lookup | Locate an exact declaration; distinguish same-named declarations; reject an absent symbol | Exact file, declaration start, terminal name, and owner; ambiguity is not a successful guess | +| Explain | Explain the declaration's role, source, incoming and outgoing relationships | Source-reviewed statements and exact endpoint identities; score unsupported extra claims as well as omissions | +| Callers | Identify direct callers, repeated call sites, and a same-named wrong-owner negative | Source call occurrences, caller-to-callee direction, multiplicity, and provenance | +| Callees | Identify direct targets, including constructors, loop receivers, and callbacks | Complete reviewed local target set for the selected function; unresolved dynamic targets stay explicit | +| Native path/walk | Find a directed call path and an undirected navigation path; handle ambiguity, disconnection, and bounds | Ordered adjacent edges, directions, source-supported occurrences, and explicit incomplete/limit outcomes | +| Natural query | Answer a source-level task with a fixed answer budget and permitted continuations | Reviewed relevant facts, unsupported returned facts, and full workflow cost | +| Ask/routing | Ask who calls a declaration, what it calls, and how two declarations connect | Same semantic witnesses as direct operations; additionally check requested direction and operation selection | +| Community lookup | Identify a selected node's community and enumerate its bounded membership | Exact identities and membership in that tool's captured graph; this is graph consistency, not source correctness | +| Cluster summary | Describe major functional groups and the connections between them | Separately reviewed source responsibilities and boundary relations; arbitrary cluster IDs/names are never gold | +| God nodes/hubs | Return top-N connected source symbols and explain their links | Independently recomputed degree/eligibility on the captured graph, plus source review of returned declarations/edges | +| Design diagnosis | Assess whether a candidate mixes responsibilities or merely has many users | Explicit source-based judgments and counterexamples; degree alone cannot establish a god-object defect | + +## Equivalent operations + +The installed versions are inspected through their own help before selecting +commands. The existing suite pairs `explain`, `path`, natural `query`, +`callers`/`affected`, and `callees`/`explain`. Both sides use the same relation +direction and comparable depth/budget wherever both expose those controls. + +Compass exposes natural `ask`, typed `search`, `architecture`, and the MCP +tools `get_neighbors`, `get_community`, `god_nodes`, and `graph_stats`. +Graphify 0.9.67 exposes CLI `god-nodes --top N --json`; its inspected CLI help +does not advertise dedicated `ask` or community-membership commands. +Its natural `query` and explanation/export workflows must be checked as the +closest available operations before declaring a task unsupported. Lack of one +command name alone does not establish lack of the capability. + +Where the available workflows use different interfaces, retain all requests, +startup/setup work, output bytes, follow-ups, and errors. Report protocol +overhead separately from answer content. A developer adapter may invoke a +documented public operation; it must not synthesize an answer by reading the +graph on behalf of only one tool. Independent graph inspection belongs to the +oracle, not the measured answer path. + +Native-only structural runs use no model-generated edges or community labels +on either side. A future model-assisted comparison requires a separate arm +with the same model, credentials policy, budget, and complete cost accounting. +Do not compare one tool's model-assisted output with the other's native output. + +## Scoring and acceptance + +- Publish category-level results and every failure, including competitor wins. +- Keep source correctness, graph consistency, task availability, and output + efficiency separate. No single combined score may hide a precision failure. +- A text anchor pass does not certify all statements in an answer. Add source + review of returned edges/owners before making precision claims. +- Score community partition metrics separately from functional usefulness. + Different valid partitions are possible; matching a package directory is at + most a structural proxy, not proof of cohesion or architectural quality. +- Compare tokens on the same successful questions and retain total continuation + cost. Bytes/4 estimates must stay labeled as estimates. +- Repeated release-build timings on quiet, matched inputs are required for + speed claims. Concurrent debug runs provide correctness evidence only. +- Existing fixes and development-suite leads do not satisfy the complete + objective. God-object judgments and the additional community/ask surfaces + still require measured evidence across the repository panel. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 6ddbbe09d..91f8674ee 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -4,13 +4,18 @@ in its suites compared with Graphify on the same pinned checkouts. It is developer-side tooling: Compass never runs it, and it never installs Graphify. -Three suites share the harness: +[`COVERAGE_PLAN.md`](COVERAGE_PLAN.md) tracks the broader real-repository +question/evidence matrix, including ask, communities, clusters, and god nodes. +Those planned surfaces must not be described as already evaluated. + +Four suites share the harness: | Suite | Questions | Shape | | --- | ---: | --- | | `suite.toml` | 47 | The first five-repository suite, including Compass's compact and paged projections | | `suite_v2.toml` | 50 | A blackbox-fair extension: same questions for both tools, default output forms, no tool-specific projections | | `suite_fd.toml` | 12 | Separate pinned `sharkdp/fd` sample, recorded from source before either tool's first extraction/query run | +| `suite_ask.toml` | 10 | Same natural-language caller/callee questions and 2,000-token budget for Compass `ask` and Graphify `query` across five languages | `suite_v2.toml` states its fairness contract inline and keeps it in the rows: both tools are blackboxes over the same pinned checkout, every oracle is read @@ -28,6 +33,12 @@ that, one tool's fixed metadata can consume the whole page. No v2 row repeats a question from the first suite; the audit compares repository, kind and addressed symbol across both files. +`suite_ask.toml` reuses reviewed caller/callee facts from v2 to compare the +natural-language interface. These are ten interface checks, not ten additional +independent source judgments. Both sides receive identical question text and +one 2,000-token response budget, with no continuations. The initial scorer checks +selected-fact recall; extra statements still need source review for precision. + The suite covers five real repositories in five languages: | Repository | Language | Focus | diff --git a/benchmarks/agent_query/suite_ask.toml b/benchmarks/agent_query/suite_ask.toml new file mode 100644 index 000000000..27febe2e5 --- /dev/null +++ b/benchmarks/agent_query/suite_ask.toml @@ -0,0 +1,227 @@ +# Natural-language routing development comparison across five real languages. +# Reuses source-reviewed caller/callee facts from suite_v2.toml; these are not +# independent held-out judgments. The exact same question and 2000-token budget +# are sent to Compass ask and Graphify query. One response, no follow-ups. +# Score selected-fact text recall first; this does not certify every returned edge. + +schema = "compass.agent-query-suite/1" + +[[repository]] +name = "cobra" +language = "Go" +url = "https://github.com/spf13/cobra.git" +commit = "adbc8813901bba65827259daa8e22ff94ec1f30e" + +[[repository.anchor]] +file = "command.go" +line = 1868 +symbol = "ParseFlags" +judgment = "command.go:1868 declares func (c *Command) ParseFlags(args []string) error." + +[[repository.anchor]] +file = "command.go" +line = 757 +symbol = "Find" +judgment = "command.go:757 declares func (c *Command) Find(args []string) (*Command, []string, error)." + +[[repository.anchor]] +file = "doc/md_docs.go" +line = 52 +symbol = "GenMarkdown" +judgment = "doc/md_docs.go:52 declares GenMarkdown, which consumes the cobra package it imports at line 27." + +[[repository.question]] +id = "cobra-ask-callers" +kind = "callers" +subject = "who calls cobra.Command::Find?" +compass = ["ask", "who calls cobra.Command::Find?", "--text-budget", "2000"] +graphify = ["query", "who calls cobra.Command::Find?", "--budget", "2000"] +expect = "answer" +required = ["ExecuteC", "command.go"] +judgment = "Same source fact as cobra2-callers-find. Find is called by ExecuteC at command.go:1123 and by InitDefaultHelpCmd (command.go:1263) at command.go:1276 and command.go:1294. This row changes the natural-language interface, not the underlying source judgment." + +[[repository.question]] +id = "cobra-ask-callees" +kind = "callees" +subject = "what does cobra.Command::ExecuteC call?" +compass = ["ask", "what does cobra.Command::ExecuteC call?", "--text-budget", "2000"] +graphify = ["query", "what does cobra.Command::ExecuteC call?", "--budget", "2000"] +expect = "answer" +required = ["Find", "execute", "Traverse"] +judgment = "Same source fact as cobra2-callees-executec. ExecuteC calls Traverse (command.go:1121), Find (command.go:1123) and execute (command.go:1148). This row changes the natural-language interface, not the underlying source judgment." + +[[repository]] +name = "flask" +language = "Python" +url = "https://github.com/pallets/flask.git" +commit = "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35" + +[[repository.anchor]] +file = "src/flask/app.py" +line = 969 +symbol = "dispatch_request" +judgment = "src/flask/app.py:969 declares Flask.dispatch_request." + +[[repository.anchor]] +file = "src/flask/ctx.py" +line = 260 +symbol = "AppContext" +judgment = "src/flask/ctx.py:260 declares AppContext." + +[[repository.anchor]] +file = "src/flask/views.py" +line = 78 +symbol = "View.dispatch_request" +judgment = "src/flask/views.py:78 declares View.dispatch_request." + +[[repository.question]] +id = "flask-ask-callers" +kind = "callers" +subject = "who calls src.flask.app.Flask::full_dispatch_request?" +compass = ["ask", "who calls src.flask.app.Flask::full_dispatch_request?", "--text-budget", "2000"] +graphify = ["query", "who calls src.flask.app.Flask::full_dispatch_request?", "--budget", "2000"] +expect = "answer" +required = ["wsgi_app"] +judgment = "Same source fact as flask2-callers-fulldispatch. wsgi_app calls self.full_dispatch_request(ctx) at src/flask/app.py:1600. This row changes the natural-language interface, not the underlying source judgment." + +[[repository.question]] +id = "flask-ask-callees" +kind = "callees" +subject = "what does src.flask.app.Flask::full_dispatch_request call?" +compass = ["ask", "what does src.flask.app.Flask::full_dispatch_request call?", "--text-budget", "2000"] +graphify = ["query", "what does src.flask.app.Flask::full_dispatch_request call?", "--budget", "2000"] +expect = "answer" +required = ["preprocess_request", "dispatch_request", "finalize_request"] +judgment = "Same source fact as flask2-callees-fulldispatch. full_dispatch_request calls preprocess_request (app.py:1017), dispatch_request (1019) and finalize_request (1022). This row changes the natural-language interface, not the underlying source judgment." + +[[repository]] +name = "gson" +language = "Java" +url = "https://github.com/google/gson.git" +commit = "15ca7360379cf3c1502b59981569050489f2d73e" + +[[repository.anchor]] +file = "gson/src/main/java/com/google/gson/Gson.java" +line = 565 +symbol = "toJson(Object)" +judgment = "Gson.java:565 declares public String toJson(Object src)." + +[[repository.anchor]] +file = "gson/src/main/java/com/google/gson/stream/JsonWriter.java" +line = 526 +symbol = "value(String)" +judgment = "JsonWriter.java:526 starts the @CanIgnoreReturnValue annotation on public JsonWriter value(String value), whose method header is line 527." + +[[repository.anchor]] +file = "gson/src/main/java/com/google/gson/TypeAdapter.java" +line = 143 +symbol = "toJson(Writer, T)" +judgment = "TypeAdapter.java:143 declares final void toJson(Writer out, T value)." + +[[repository.question]] +id = "gson-ask-callers" +kind = "callers" +subject = "who calls com.google.gson.Gson::newJsonWriter?" +compass = ["ask", "who calls com.google.gson.Gson::newJsonWriter?", "--text-budget", "2000"] +graphify = ["query", "who calls com.google.gson.Gson::newJsonWriter?", "--budget", "2000"] +expect = "answer" +required = ["toJson", "Gson.java"] +judgment = "Same source fact as gson2-callers-newjsonwriter. newJsonWriter is called by the toJson overloads at Gson.java:642 and Gson.java:724. This row changes the natural-language interface, not the underlying source judgment." + +[[repository.question]] +id = "gson-ask-callees" +kind = "callees" +subject = "what does com.google.gson.Gson::newJsonWriter call?" +compass = ["ask", "what does com.google.gson.Gson::newJsonWriter call?", "--text-budget", "2000"] +graphify = ["query", "what does com.google.gson.Gson::newJsonWriter call?", "--budget", "2000"] +expect = "answer" +required = ["setHtmlSafe", "setSerializeNulls"] +judgment = "Same source fact as gson2-callees-newjsonwriter. newJsonWriter calls setFormattingStyle (Gson.java:802), setHtmlSafe (803), setStrictness (804) and setSerializeNulls (805) on the writer it returns. This row changes the natural-language interface, not the underlying source judgment." + +[[repository]] +name = "zod" +language = "TypeScript" +url = "https://github.com/colinhacks/zod.git" +commit = "d2b135cfb7a3582b9eb515756b9166bcb9521f4a" + +[[repository.anchor]] +file = "packages/zod/src/v4/classic/schemas.ts" +line = 303 +symbol = "ZodType.safeParse" +judgment = "packages/zod/src/v4/classic/schemas.ts:303 implements ZodType.safeParse." + +[[repository.anchor]] +file = "packages/zod/src/v4/classic/from-json-schema.ts" +line = 105 +symbol = "detectVersion" +judgment = "packages/zod/src/v4/classic/from-json-schema.ts:105 declares detectVersion." + +[[repository.anchor]] +file = "packages/zod/src/v4/classic/parse.ts" +line = 22 +symbol = "safeParse" +judgment = "packages/zod/src/v4/classic/parse.ts:22 exports the classic safeParse entry point." + +[[repository.question]] +id = "zod-ask-callers" +kind = "callers" +subject = "who calls detectVersion?" +compass = ["ask", "who calls detectVersion?", "--text-budget", "2000"] +graphify = ["query", "who calls detectVersion?", "--budget", "2000"] +expect = "answer" +required = ["fromJSONSchema", "from-json-schema.ts"] +judgment = "Same source fact as zod2-callers-detectversion. fromJSONSchema calls detectVersion(normalized, params?.defaultTarget) at from-json-schema.ts:931. This row changes the natural-language interface, not the underlying source judgment." + +[[repository.question]] +id = "zod-ask-callees" +kind = "callees" +subject = "what does convertSchema call?" +compass = ["ask", "what does convertSchema call?", "--text-budget", "2000"] +graphify = ["query", "what does convertSchema call?", "--budget", "2000"] +expect = "answer" +required = ["convertBaseSchema"] +judgment = "Same source fact as zod2-callees-convertschema. convertSchema calls convertBaseSchema(schema, ctx) at from-json-schema.ts:814. This row changes the natural-language interface, not the underlying source judgment." + +[[repository]] +name = "axum" +language = "Rust" +url = "https://github.com/tokio-rs/axum.git" +commit = "af1345b53a259b0990be1ff853f9b56c05040ef7" + +[[repository.anchor]] +file = "src/routing/mod.rs" +line = 192 +symbol = "Router::route" +judgment = "src/routing/mod.rs:192 declares pub fn route(self, path: &str, method_router: MethodRouter) -> Self." + +[[repository.anchor]] +file = "src/routing/path_router.rs" +line = 22 +symbol = "validate_path" +judgment = "src/routing/path_router.rs:22 declares fn validate_path." + +[[repository.anchor]] +file = "src/serve/mod.rs" +line = 106 +symbol = "serve" +judgment = "src/serve/mod.rs:106 declares pub fn serve." + +[[repository.question]] +id = "axum-ask-callers" +kind = "callers" +subject = "who calls validate_path?" +compass = ["ask", "who calls validate_path?", "--text-budget", "2000"] +graphify = ["query", "who calls validate_path?", "--budget", "2000"] +expect = "answer" +required = ["route"] +judgment = "Same source fact as axum2-callers-validate-path. validate_path is called by PathRouter::route at path_router.rs:71 and PathRouter::route_endpoint at line 125. This row changes the natural-language interface, not the underlying source judgment." + +[[repository.question]] +id = "axum-ask-callees" +kind = "callees" +subject = "what does validate_path call?" +compass = ["ask", "what does validate_path call?", "--text-budget", "2000"] +graphify = ["query", "what does validate_path call?", "--budget", "2000"] +expect = "answer" +required = ["validate_v07_paths"] +judgment = "Same source fact as axum2-callees-validate-path. validate_path (path_router.rs:22) calls validate_v07_paths(path) at path_router.rs:30, declared at path_router.rs:36. This row changes the natural-language interface, not the underlying source judgment." diff --git a/benchmarks/agent_query/tests/test_runner.py b/benchmarks/agent_query/tests/test_runner.py index abf248e27..6e25758b0 100644 --- a/benchmarks/agent_query/tests/test_runner.py +++ b/benchmarks/agent_query/tests/test_runner.py @@ -51,6 +51,19 @@ def question(**overrides) -> Question: class SuiteTests(unittest.TestCase): + def test_ask_suite_uses_identical_questions_and_budgets(self) -> None: + suite = load_suite(ROOT / "suite_ask.toml") + self.assertEqual({r.language for r in suite.repositories}, + {"Go", "Python", "Java", "TypeScript", "Rust"}) + for repository in suite.repositories: + self.assertEqual({q.kind for q in repository.questions}, {"callers", "callees"}) + self.assertEqual(len(repository.questions), 2) + for question in repository.questions: + self.assertEqual(question.compass, ("ask", question.subject, "--text-budget", "2000")) + self.assertEqual(question.graphify, ("query", question.subject, "--budget", "2000")) + self.assertEqual(question.max_follow_ups, 0) + self.assertIn("Same source fact", question.judgment) + def test_fd_source_first_inputs_remain_distinct_and_pinned(self) -> None: suite = load_suite(ROOT / "suite_fd.toml") self.assertEqual(len(suite.repositories), 1) From 1032fde1fb2eeb221c37f02717a3db556b2091fe Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 15:56:09 -0700 Subject: [PATCH 10/97] fix: preserve query subjects in agent answers --- CHANGELOG.md | 6 + COMPATIBILITY.md | 11 +- MIGRATION.md | 5 + benchmarks/agent_query/COVERAGE_PLAN.md | 16 ++- crates/compass-cli/src/code_query_commands.rs | 34 +++-- crates/compass-cli/tests/code_query_cli.rs | 63 ++++++++++ crates/compass-output/src/agent_query.rs | 8 +- crates/compass-output/tests/agent_query.rs | 55 +++++++- crates/compass-query/src/code_query.rs | 5 +- crates/compass-query/src/lib.rs | 2 +- ...ode-graph-intelligence-audit-2026-09-26.md | 117 +++++++++++++++++- 11 files changed, 302 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a46cd5692..e3e1da25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Preserve parsed `ask` operands in agent and text answers so headlines, + answer evidence, path endpoints, and follow-up actions describe the requested + symbols. +- Match agent-answer subjects using the query engine's existing case and + function-label normalization, including names such as `convertSchema()`. + - Prevent Rust local receiver bindings from inheriting shadowed outer parameter types in lets, loops, closures, match arms/guards, and conditional lets. Preserve initializer and else-branch scope, and rebuild older AST caches. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index d77e57efc..e918d36e9 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -658,7 +658,16 @@ realizations of the backend-neutral `compass-store` contract, not a stable SQL schema or pointer format that consumers may query directly. The additive `compass ask` command continues to route bounded questions to the -typed `compass.query/1` operations. Plain `compass query` against a typed graph +typed `compass.query/1` operations. Its agent projections now retain the parsed +symbol/source/target operands used by that operation, with the original question +in `request.question`. This corrects headlines, evidence basis, and follow-up +actions that previously treated the whole question as a symbol. Agent-answer +subject lookup shares the query engine's existing name normalization (case, +leading dots, and trailing empty parentheses), retaining exact node-ID lookup +and requiring a unique normalized name. Existing schema +majors and raw query responses are unchanged. Text cursors whose primary ordering +changed are rejected by the existing prefix check; reissue the question. +Plain `compass query` against a typed graph now defaults to `compass.query.discovery/1`; `--dfs` and `--context` compose with discovery. Explicit `--traverse` or legacy-only `--budget`/`--page` preserve the established text traversal and reject discovery controls. diff --git a/MIGRATION.md b/MIGRATION.md index 5d75d8f6d..d39a96a21 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,11 @@ layout remains visible and clearly owned. ## Query text and path resolution +`ask` agent output now records parsed operands in `request.operands` and the +original question in `request.question`. Consumers needing the question should +read that dedicated field. If an existing text cursor fails its prefix check +after the corrected subject ordering, reissue the question to start a new page. + Plain `compass query` output is now concise by default and its page budget is 8,000 approximate tokens. Scripts or review workflows that need the previous expanded provenance should pass `--evidence`. Existing diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 56affecbe..72f1c7e93 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -44,9 +44,19 @@ Compass exposes natural `ask`, typed `search`, `architecture`, and the MCP tools `get_neighbors`, `get_community`, `god_nodes`, and `graph_stats`. Graphify 0.9.67 exposes CLI `god-nodes --top N --json`; its inspected CLI help does not advertise dedicated `ask` or community-membership commands. -Its natural `query` and explanation/export workflows must be checked as the -closest available operations before declaring a task unsupported. Lack of one -command name alone does not establish lack of the capability. +Further installed-source inspection and `python -m graphify.serve --help` +confirm a public MCP server in `graphify.serve`, including `get_neighbors`, +`get_community`, `god_nodes`, and `graph_stats`. These can be compared directly +with the matching Compass MCP tools. The currently pinned Graphify environment +lacks the optional `mcp` SDK. An isolated `graphifyy[mcp]==0.9.67` environment +now provides it; all 227 compared Graphify package files match the original +installation. The environment manifest pins its separate dependencies. Actual +MCP initialize/tools-list handshakes succeeded for both tools on the retained +Cobra graphs, confirming all four named tools. This is interface availability +evidence; the cross-language community/hub questions still need execution. +Graphify community lookup accepts an +explicit token budget; account for that bound and any truncation separately. +Lack of one CLI command name does not establish lack of the capability. Where the available workflows use different interfaces, retain all requests, startup/setup work, output bytes, follow-ups, and errors. Report protocol diff --git a/crates/compass-cli/src/code_query_commands.rs b/crates/compass-cli/src/code_query_commands.rs index ac264272a..e6960f98e 100644 --- a/crates/compass-cli/src/code_query_commands.rs +++ b/crates/compass-cli/src/code_query_commands.rs @@ -11,8 +11,8 @@ use compass_output::{ render_code_query_text_page, }; use compass_query::{ - EngineSelection, NaturalQueryRequest, QueryError, QueryErrorKind, open_with_engine, - open_with_verified_document, + EngineSelection, NaturalQueryIntent, NaturalQueryRequest, QueryError, QueryErrorKind, + open_with_engine, open_with_verified_document, plan_natural_query, }; use crate::{Outcome, SharedOutputFormat, parse_shared_output_format}; @@ -222,6 +222,30 @@ fn execute( let (response, question, operands) = match operation { "ask" => { let question = required(&positional, 0, "ask ")?.to_owned(); + let plan = plan_natural_query(&question).map_err(query_error)?; + // The renderer needs the same operands the query engine executes. + // The full question is retained separately as request metadata. + let operands = if plan.routes_to_typed_query() { + plan.operands() + .iter() + .enumerate() + .map(|(index, value)| { + let role = match plan.intent() { + NaturalQueryIntent::Callers + | NaturalQueryIntent::Callees + | NaturalQueryIntent::Impact => AgentOperandRole::Symbol, + NaturalQueryIntent::NodeTrail if index == 0 => AgentOperandRole::Source, + NaturalQueryIntent::NodeTrail => AgentOperandRole::Target, + NaturalQueryIntent::Search | NaturalQueryIntent::Fallback => { + AgentOperandRole::Query + } + }; + (role, value.clone()) + }) + .collect() + } else { + vec![(AgentOperandRole::Query, question.clone())] + }; let response = engine .query_natural(NaturalQueryRequest { question: question.clone(), @@ -229,11 +253,7 @@ fn execute( limits, }) .map_err(query_error)?; - ( - response, - Some(question.clone()), - vec![(AgentOperandRole::Query, question)], - ) + (response, Some(question), operands) } "search" => { let query = required(&positional, 0, "search ")?.to_owned(); diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 743ea8435..b991a9e0e 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -11,6 +11,69 @@ use compass_output::{AgentOperation, AgentQueryView}; use compass_store::{STORE_FILE_NAME, STORE_REF_FILE_NAME, SqliteStore}; use serde_json::Value; +#[test] +fn ask_preserves_typed_operands_in_agent_and_text_answers() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph = support::write_typed_graph(directory.path())?; + for (question, command, operands) in [ + ("who calls Target?", "callers", vec!["Target"]), + ("who calls Missing?", "callers", vec!["Missing"]), + ("what does Caller call?", "callees", vec!["Caller"]), + ("what is impacted by Target?", "impact", vec!["Target"]), + ( + "path from Caller to Target", + "node", + vec!["Caller", "Target"], + ), + ("where is Target defined?", "search", vec!["Target"]), + ] { + for format in ["agent-json", "text"] { + let execute = |command: &str, operands: &[&str]| { + let mut args = vec![OsString::from(command)]; + args.extend(operands.iter().map(OsString::from)); + args.extend([ + OsString::from("--graph"), + graph.as_os_str().to_owned(), + OsString::from("--format"), + OsString::from(format), + ]); + run(Frontend::Compass, args) + }; + let asked = execute("ask", &[question]); + let direct = execute(command, &operands); + assert_eq!(asked.code, 0, "{question}: {}", asked.stderr); + assert_eq!(direct.code, 0, "{}", direct.stderr); + if format == "agent-json" { + let asked = AgentQueryView::from_json(asked.stdout.as_bytes())?; + let direct = AgentQueryView::from_json(direct.stdout.as_bytes())?; + assert_eq!(asked.answer, direct.answer, "{question}"); + assert_eq!( + asked.request.operands, direct.request.operands, + "{question}" + ); + assert_eq!(asked.primary_results, direct.primary_results, "{question}"); + assert_eq!(asked.paths, direct.paths, "{question}"); + assert_eq!(asked.next_actions, direct.next_actions, "{question}"); + assert_eq!(asked.request.question.as_deref(), Some(question)); + } else { + // Compare the answer separately from pagination metadata. + let headline = direct + .stdout + .lines() + .skip_while(|line| *line != "ANSWER") + .nth(1) + .ok_or("missing direct answer")?; + assert!( + asked.stdout.contains(headline), + "{question}: {}", + asked.stdout + ); + } + } + } + Ok(()) +} + #[test] fn typed_query_commands_share_the_versioned_json_contract() -> Result<(), Box> { let directory = tempfile::tempdir()?; diff --git a/crates/compass-output/src/agent_query.rs b/crates/compass-output/src/agent_query.rs index 2aade6dca..93226eed2 100644 --- a/crates/compass-output/src/agent_query.rs +++ b/crates/compass-output/src/agent_query.rs @@ -11,7 +11,7 @@ use compass_model::query_contract::{ }; use compass_query::{ CursorTokenError, code_query_response_digest, decode_cursor_token, discovery_response_digest, - encode_cursor_token, + encode_cursor_token, normalize_code_query_symbol, }; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value, json}; @@ -2211,9 +2211,13 @@ fn unique_node_id(value: &str, nodes: &BTreeMap) -> Option>(); matches.sort(); diff --git a/crates/compass-output/tests/agent_query.rs b/crates/compass-output/tests/agent_query.rs index 0770ff9fd..b797f2152 100644 --- a/crates/compass-output/tests/agent_query.rs +++ b/crates/compass-output/tests/agent_query.rs @@ -63,6 +63,58 @@ fn context(operation: AgentOperation) -> AgentQueryContext { AgentQueryContext::new(operation, "graph-identity", "generation-identity") } +#[test] +fn relationship_subject_uses_the_query_engines_symbol_normalization() -> Result<(), Box> +{ + let source = anchor("src/lib.rs", 1); + for (operation, agent_operation) in [ + (CodeQueryOperation::Callers, AgentOperation::Callers), + (CodeQueryOperation::Callees, AgentOperation::Callees), + ] { + let mut response = response(operation); + response.nodes = vec![ + node("a:neighbor", "Neighbor", &source), + node("z:subject", ".Subject()", &source), + ]; + response.nodes[1].qualified_name = "Fixture.Subject".to_owned(); + let (from, to) = if operation == CodeQueryOperation::Callers { + ("a:neighbor", "z:subject") + } else { + ("z:subject", "a:neighbor") + }; + response.edges.push(QueryEdge { + id: "e:call".to_owned(), + source: from.to_owned(), + target: to.to_owned(), + kind: EdgeKind::Calls, + relationship_site: Some(source.clone()), + details: None, + evidence: vec![evidence(&source)], + }); + for query in [ + "Subject", + "subject()", + ".SUBJECT()", + " Fixture.Subject ", + "z:subject", + ] { + let view = build_code_query_view( + &response, + context(agent_operation) + .with_operand(compass_output::AgentOperandRole::Symbol, query), + )?; + assert!( + view.answer.headline.ends_with("for Fixture.Subject."), + "{query}: {}", + view.answer.headline + ); + assert_eq!(view.answer.basis[0].id, "z:subject"); + assert_eq!(view.primary_results[0].id, "z:subject"); + } + } + Ok(()) +} + #[test] fn direct_usage_survives_the_projection_bound_ahead_of_owner_references() -> Result<(), Box> { @@ -790,7 +842,8 @@ fn legacy_page_cursor_encoding_is_rejected_with_a_version_error() -> Result<(), let checksum = format!("{:x}", Sha256::digest(payload.as_bytes())); let cursor = format!("{payload}.{checksum}"); let error = compass_output::decode_agent_text_page_cursor(&cursor) - .expect_err("a legacy cursor must not be reinterpreted"); + .err() + .ok_or("a legacy cursor must not be reinterpreted")?; let message = error.to_string(); assert!( message.contains("cursor"), diff --git a/crates/compass-query/src/code_query.rs b/crates/compass-query/src/code_query.rs index 9236a2edd..3c193959f 100644 --- a/crates/compass-query/src/code_query.rs +++ b/crates/compass-query/src/code_query.rs @@ -3751,7 +3751,10 @@ fn path_record(nodes: &[String], edges: &[String], selected: &[EdgeRecord]) -> Q } } -pub(crate) fn normalize_symbol(value: &str) -> String { +/// Canonical name comparison shared by typed resolution and its projections. +/// Node IDs are matched exactly before this normalization is applied to names. +#[must_use] +pub fn normalize_symbol(value: &str) -> String { value .trim() .trim_end_matches("()") diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index a14d3c766..56242a3e2 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -25,7 +25,7 @@ mod traversal; pub use affected::{DEFAULT_AFFECTED_RELATIONS, affected_nodes, format_affected, resolve_seed}; pub use benchmark::{BenchmarkQuestion, BenchmarkResult, format_benchmark, run_benchmark}; -pub use code_query::CodeQueryEngine; +pub use code_query::{CodeQueryEngine, normalize_symbol as normalize_code_query_symbol}; pub use cql::{ CacheStats, ExplainPlan, OperatorProfile, PlanCache, PlanCacheConfig, QueryError, QueryErrorKind, QueryLimits, QueryProfile, QueryRequest, QueryResult, execute, diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index cd4c54a50..62894c18d 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -409,13 +409,122 @@ incorrect relationships. A cache regression checks that version-2 facts are discarded. This is a correction to the existing producer contract, not a new advertised capability or a new universal-pipeline promotion. Evidence/graph schema majors and the Rust producer capability identity remain unchanged; -historical graphs are not rewritten. Full baseline, fixture, and fresh binary -replay evidence for this correction is recorded below as it completes. +historical graphs are not rewritten. + +The full workspace native baseline passes 1,083 tests with zero failures and +two ignored tests. The qualifying debug executable was copied and hashed after +its successful native build; its source snapshot, patch, and committed Rust +file hashes are retained under `shadow-fix-provenance`. The replay in +`rust-shadow-corrected-03` removes the wrong `Decoy` edge in both source +reductions while preserving exactly one valid outer call in each. The still +unproven inner receiver remains unresolved. + +Fresh extraction/query run `fd-shadow-02` preserves all 12 query outcomes, +all 16 reviewed Compass call occurrences, and both source-grounded path +witnesses. The paired token medians remain 227 versus 88 on eight shared +passes. The separate diagnostic still records the three missing Compass +loop/callback calls and Graphify's wrong constructor target. All 92 recorded +Graphify distribution-file hashes remained unchanged; this does not pin every +transitive dependency. The Compass executable is a debug build and native +qualification ran concurrently, so these timings are not performance evidence. + +Fixture qualification initially stopped at preflight because the default +parser-source bundle directory was absent. It was restarted using the complete +bundle already present in this checkout's build directory, whose language +definition hash matches the vendored manifest. The full fixture gate completed +successfully, including independent Markdown and React frontend qualification. +The release executable is frozen under `shadow-fix-provenance/compass-release` +with SHA256 `6baa1cabccdea6357ad5e9653a008efa1b384250557c1ce27278402e85399719`. + +## Natural-language interface comparison + +The preregistered `suite_ask.toml` sends identical caller/callee questions and +2,000-token budgets through Compass `ask` and Graphify `query`, with no +continuations. The ten questions reuse reviewed facts from the five-language +panel; they are interface checks, not ten independent source judgments. +Captured run `ask-paired-01` passes 9/10 selected-fact text checks for Compass +and 10/10 for Graphify. Compass's Cobra callees page omits `Find` before its +12-primary-result projection bound, even though the requested token budget +has room. Graphify wins that preregistered row. Do not change its oracle or +silently add a follow-up to erase this failure. + +Review beyond the text oracle finds seven Compass relationship headlines +attributing the answer to the wrong subject: Cobra callers, both Flask rows, +both Gson rows, Zod callees, and Axum callees. Their correct fact anchors do +not make these answers fully correct. The CLI executes parsed operands but +supplies the whole natural-language question as a query operand to the +renderer; the first node by ID then substitutes for the requested subject. +This also affects answer basis, primary ordering, and suggested next actions. +The native CLI regression reproduced the wrong subject before correction. +The CLI now passes the planner operands with their symbol/source/target roles +and retains the original question separately. The first correction passes +36 CLI query and 9 product tests, workspace Clippy, and the 1,083-test native +baseline. The expanded regression also checks a missing subject and follow-up +actions. Python benchmark tests pass 49 cases. + +Graphify exceeds the requested 2,000-token budget on four answers (Cobra +callers: 2,854; Gson callers and callees: 2,773 each; Axum callees: 3,160), +using the recorded stdout bytes/4 estimate. Each explicitly discloses the +overrun. The original selected-fact scores remain recorded; they are not +hard-budget success scores or complete precision judgments. On the nine +shared text passes, median output is 291 versus 1,210 estimated tokens. +This does not establish equal-budget correctness or performance superiority. + +The diagnostic query-only replay `ask-operands-replay-02` uses retained, +digest-checked graphs from `ask-paired-01`. Both tools now pass 10/10 text +checks, including Cobra: correct subject ordering puts its missing fact on +page one without changing the budget or oracle. Median text output is 279.5 +versus 1,439.5 estimated tokens on the ten shared passes. Additional JSON +requests compare all ten Compass ask projections with their direct commands; +those diagnostic requests are excluded from paired workflow costs. + +All ten agree with direct commands, but source review still finds two wrong +headlines in both interfaces (Zod callees and Axum callees): function labels +such as `convertSchema()` and `validate_path()` carry trailing parentheses, +and the renderer's exact-string lookup does not share the query engine's +symbol normalization. Consequently Zod still names `convertBaseSchema` and Axum still names +`validate_v07_paths` as their subjects. The first correction resolves five of +seven observed headline failures; direct-command equivalence alone cannot prove source correctness. +A separate renderer regression failed before correction. The renderer now +shares the query engine's existing normalization for case, leading dots, and +trailing empty parentheses, while exact IDs and uniqueness remain explicit. +The original run, intermediate replay, and their scores remain retained +independently. + +Final query-only replay `ask-operands-replay-03` passes 10/10 text checks for +both tools. A separate post-output subject diagnostic verifies all ten Compass +headlines and node bases against exact reviewed declaration labels, files, +and start lines (including Zod and Axum). Those witnesses are retained as +`ask-subject-witnesses.json`; they do not replace the original text oracle. +The frozen final debug executable has SHA256 +`a748d0fa8eb08fec09a43647e29be0b056a706eb986d70c47049463c9dbb123c`. +The source patch and file hashes are retained under +`ask-normalization-provenance`. Output medians remain 279.5 versus 1,439.5 +estimated tokens on the same ten passing text questions. + +The complete native run passes 1,145 tests: 1,083 workspace lib/bin tests, +36 CLI query tests, 9 CLI product tests, and 17 output integration tests; +two existing tests remain ignored. Workspace and changed integration-target +Clippy pass with warnings denied. The first expanded Clippy run found an +existing `expect_err` in the touched output test; it now returns the failed +assertion as an error. The 17-test output suite was rerun after that test-only +cleanup. Formatting, diff checks, product boundary, and 49 Python benchmark +tests pass. The full extraction fixture gate passed at the preceding receiver +checkpoint; these presentation corrections use native query/CLI tests and +retained real graphs rather than claiming a new extraction qualification. + +The fresh `v2-shadow-05` run remains 50/50 versus 44/50, with five source +excerpt availability differences and one Axum file-resolution difference. +Its five source-grounded path witnesses pass for both tools in +`path-audit-v2-shadow-05.json`. Graphify uses less output on its 44 shared +passes (112 versus 308 estimated tokens). All of these runs use a frozen +debug executable during concurrent qualification; timings are not speed +comparisons. The new ask failures demonstrate why that earlier suite is +insufficient to establish the requested superiority. ## Next evidence to collect -1. Complete qualification and fresh binary replay of receiver shadowing, then - extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact +1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. 2. Expand hub review beyond candidate eligibility to source-reviewed design From 152efdd782d9bda154c6257498f41c8172872ec6 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:02:00 -0700 Subject: [PATCH 11/97] bench: preregister multilingual MCP graph questions --- benchmarks/agent_query/mcp_compare.py | 140 ++++++++++++++++++ benchmarks/agent_query/mcp_transport.py | 115 ++++++++++++++ benchmarks/agent_query/suite_mcp.json | 20 +++ .../agent_query/tests/test_mcp_transport.py | 44 ++++++ 4 files changed, 319 insertions(+) create mode 100644 benchmarks/agent_query/mcp_compare.py create mode 100644 benchmarks/agent_query/mcp_transport.py create mode 100644 benchmarks/agent_query/suite_mcp.json create mode 100644 benchmarks/agent_query/tests/test_mcp_transport.py diff --git a/benchmarks/agent_query/mcp_compare.py b/benchmarks/agent_query/mcp_compare.py new file mode 100644 index 000000000..03b3ed445 --- /dev/null +++ b/benchmarks/agent_query/mcp_compare.py @@ -0,0 +1,140 @@ +"""Capture preregistered, public MCP operations on existing paired graphs. + +This collector does not synthesize tool answers. Input IDs/community IDs are +prepared symmetrically from captured graphs; that preparation is not scored as +node retrieval. It requires separately supplied local server executables. +""" +from __future__ import annotations +import argparse +import json +from pathlib import Path +import shutil +import time +from collections import Counter + +from benchmarks.agent_query.mcp_transport import StdioMcp +from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES, check_source +from benchmarks.agent_query.runner import _sha256_file, _node_anchor, _verify_source, load_suite + + +def community(node, tool): + value = node.get('community') + return value.get('id') if tool == 'compass' and isinstance(value, dict) else value + + +def prepare_questions(graph, tool, witness): + matches = [n for n in graph['nodes'] if _node_anchor(n, tool)[:2] == (witness['file'], witness['line']) + and witness['symbol'] in _node_anchor(n, tool)[2]] + if len(matches) != 1: + raise ValueError(f"{tool}: declaration input has {len(matches)} identities") + counts = Counter(community(n, tool) for n in graph['nodes'] if community(n, tool) is not None) + if not counts or any(type(k) is not int or k < 0 for k in counts): + raise ValueError('invalid or missing stored community IDs') + largest = min(counts, key=lambda k: (-counts[k], k)) + budget = {'token_budget': 262144} if tool == 'graphify' else {} + queries = [ + ('stats', 'graph_stats', {}), + ('hubs', 'god_nodes', {'top_n': 10}), + ('community', 'get_community', {'community_id': largest, **budget}), + ('missing-community', 'get_community', {'community_id': max(counts)+1, **budget}), + ('neighbors', 'get_neighbors', {'label': matches[0]['id'], 'relation_filter': 'calls', **budget}), + ] + if witness['ambiguousLabel']: + queries.append(('ambiguous-neighbors', 'get_neighbors', {'label': witness['ambiguousLabel'], **budget})) + return queries + + +def verify_environment(args): + manifest = json.loads(read_bounded(args.graphify_environment)) + roots = list(args.graphify_python.parent.parent.glob('lib/python*/site-packages/graphify')) + if len(roots) != 1: + raise ValueError('cannot identify Graphify package root') + for record in manifest['files']: + relative = Path(record['file']) + if relative.is_absolute() or '..' in relative.parts: + raise ValueError('invalid environment file path') + if _sha256_file(roots[0]/relative) != record['mcpEnvironmentSha256']: + raise ValueError('Graphify package changed since environment capture') + + +def execute(args): + verify_environment(args) + source_run = json.loads(read_bounded(args.run/'run.json')) + suite = load_suite(args.run/'suite.toml') + if suite.digest != source_run['suiteDigest']: + raise ValueError('captured suite digest mismatch') + manifest = json.loads(read_bounded(args.inputs)) + if manifest['schema'] != 'compass.mcp-comparison-inputs/1': + raise ValueError('unsupported MCP input schema') + args.output.mkdir(parents=True, exist_ok=False) + shutil.copy2(args.inputs, args.output/'inputs.json') + shutil.copy2(args.graphify_environment, args.output/'graphify-environment.json') + for name in ['mcp_compare.py', 'mcp_transport.py']: + shutil.copy2(Path(__file__).with_name(name), args.output/name) + report = {'schema':'compass.mcp-comparison-capture/1', 'sourceRunSha256':_sha256_file(args.run/'run.json'), + 'inputSha256':_sha256_file(args.inputs), 'sourceRun':str(args.run.resolve()), + 'collectorSha256':_sha256_file(Path(__file__)), 'servers':{}, 'results':[]} + for tool, binary in [('compass',args.compass),('graphify',args.graphify_python)]: + report['servers'][tool] = {'executable':str(binary.resolve()), 'executableSha256':_sha256_file(binary)} + for witness in manifest['repositories']: + name = witness['name'] + if name not in {'cobra','flask','gson','zod','axum'}: + raise ValueError('unsupported repository key') + repo = next(r for r in source_run['repositories'] if r['repository'] == name) + pinned = next(r for r in suite.repositories if r.name == name) + source = Path(repo['source']) + _verify_source(pinned,source) + check_source(source, {'file':witness['file'],'line':witness['line'],'text':witness['sourceText']}) + for tool in ['compass','graphify']: + graph_path = Path(repo[tool+'Graph']) + digest = _sha256_file(graph_path) + if digest != repo[tool+'GraphSha256']: + raise ValueError('captured graph changed') + graph = json.loads(read_bounded(graph_path,MAX_GRAPH_BYTES)) + questions = prepare_questions(graph,tool,witness) + argv = [str(args.compass),'serve'] if tool == 'compass' else [str(args.graphify_python),'-m','graphify.serve'] + argv += ['--graph',str(graph_path),'--transport','stdio'] + directory = args.output/'raw'/name/tool + with StdioMcp(argv,source,directory) as session: + session.initialize() + listing = session.send('tools/list',{}) + advertised = {t['name'] for t in listing.get('result',{}).get('tools',[])} + for identifier, method, params in questions: + if method not in advertised: + raise ValueError(f'{tool} did not advertise {method}') + started = time.monotonic() + row = {'repository':name,'tool':tool,'question':identifier,'method':method, + 'arguments':params,'graphSha256':digest,'argv':argv} + try: + response = session.send('tools/call',{'name':method,'arguments':params}) + result = response.get('result',{}) + text = '\n'.join(c['text'] for c in result.get('content',[]) if c.get('type') == 'text') + row.update({'response':response,'text':text,'textBytes':len(text.encode()), + 'protocolBytes':len(json.dumps(response).encode()), + 'executionSucceeded': 'error' not in response and not result.get('isError',False)}) + except (ValueError,RuntimeError,TimeoutError,OSError) as error: + row.update({'executionSucceeded':False,'captureError':str(error)}) + row['wallMs'] = round((time.monotonic()-started)*1000) + report['results'].append(row) + (args.output/'run.json').write_text(json.dumps(report,indent=2,sort_keys=True)+'\n') + print(name,tool,identifier,row['executionSucceeded'],row.get('textBytes'),flush=True) + if 'captureError' in row: + break # A desynchronized connection cannot be reused. + if _sha256_file(graph_path) != digest: + raise ValueError('graph changed during MCP questions') + _verify_source(pinned,source) + verify_environment(args) + report['graphifyEnvironmentSha256'] = _sha256_file(args.graphify_environment) + report['complete'] = True + (args.output/'run.json').write_text(json.dumps(report,indent=2,sort_keys=True)+'\n') + + +if __name__ == '__main__': + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--run',type=Path,required=True) + p.add_argument('--inputs',type=Path,default=Path(__file__).with_name('suite_mcp.json')) + p.add_argument('--output',type=Path,required=True) + p.add_argument('--compass',type=Path,required=True) + p.add_argument('--graphify-python',type=Path,required=True) + p.add_argument('--graphify-environment',type=Path,required=True) + execute(p.parse_args()) diff --git a/benchmarks/agent_query/mcp_transport.py b/benchmarks/agent_query/mcp_transport.py new file mode 100644 index 000000000..51b747888 --- /dev/null +++ b/benchmarks/agent_query/mcp_transport.py @@ -0,0 +1,115 @@ +"""Bounded, local stdio MCP client for developer-side comparisons.""" +from __future__ import annotations + +import json +import os +from pathlib import Path +import selectors +import signal +import subprocess +import time + +MAX_BYTES = 16 * 1024 * 1024 +MAX_SESSION_BYTES = 64 * 1024 * 1024 + + +class StdioMcp: + def __init__(self, argv: list[str], cwd: Path, directory: Path, + timeout: float = 60, max_bytes: int = MAX_BYTES): + self.argv, self.cwd, self.directory = argv, cwd, directory + self.timeout, self.max_bytes = timeout, max_bytes + self.sequence, self.total, self.errors = 0, 0, 0 + self.buffer = b"" + + def __enter__(self): + self.directory.mkdir(parents=True, exist_ok=False) + self.process = subprocess.Popen(self.argv, cwd=self.cwd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + start_new_session=True) + self.selector = selectors.DefaultSelector() + self.selector.register(self.process.stdout, selectors.EVENT_READ, "stdout") + self.selector.register(self.process.stderr, selectors.EVENT_READ, "stderr") + self.error_file = (self.directory / "stderr").open("wb") + return self + + def __exit__(self, *_): + # Bound cleanup even if a server fails its handshake or ignores EOF. + if self.process.stdin: + self.process.stdin.close() + try: + self.process.wait(timeout=1) + except subprocess.TimeoutExpired: + os.killpg(self.process.pid, signal.SIGTERM) + try: + self.process.wait(timeout=1) + except subprocess.TimeoutExpired: + os.killpg(self.process.pid, signal.SIGKILL) + self.process.wait(timeout=2) + self.selector.close() + self.error_file.close() + self.process.stdout.close() + self.process.stderr.close() + + def send(self, method: str, params: dict, *, notification: bool = False): + self.sequence += 1 + request = {"jsonrpc": "2.0", "method": method, "params": params} + if not notification: + request["id"] = self.sequence + data = json.dumps(request, ensure_ascii=False).encode() + b"\n" + if len(data) > 4096: + raise ValueError("MCP evaluation request exceeds 4096 bytes") + (self.directory / f"{self.sequence:02}.request.json").write_bytes(data) + self.process.stdin.write(data) + self.process.stdin.flush() + if notification: + return None + deadline = time.monotonic() + self.timeout + received = 0 + notifications = 0 + with (self.directory / f"{self.sequence:02}.response.jsonl").open("wb") as capture: + while True: + if b"\n" in self.buffer: + line, self.buffer = self.buffer.split(b"\n", 1) + packet = json.loads(line) + if not isinstance(packet, dict) or packet.get("jsonrpc") != "2.0": + raise ValueError("invalid JSON-RPC response") + if "id" not in packet: + notifications += 1 + if notifications > 128: + raise ValueError("MCP notification bound exceeded") + continue + if packet["id"] != self.sequence: + raise ValueError("MCP response ID does not match request") + return packet + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError("MCP request deadline exceeded") + for key, _ in self.selector.select(remaining): + chunk = os.read(key.fd, 65536) + if not chunk: + self.selector.unregister(key.fileobj) + if key.data == "stdout": + raise RuntimeError("MCP server closed stdout before response") + continue + self.total += len(chunk) + if self.total > MAX_SESSION_BYTES: + raise ValueError("MCP session output bound exceeded") + if key.data == "stderr": + self.errors += len(chunk) + self.error_file.write(chunk[:max(0, self.max_bytes - self.errors + len(chunk))]) + if self.errors > self.max_bytes: + raise ValueError("MCP stderr bound exceeded") + else: + received += len(chunk) + capture.write(chunk[:max(0, self.max_bytes - received + len(chunk))]) + if received > self.max_bytes: + raise ValueError("MCP response byte bound exceeded") + self.buffer += chunk + + def initialize(self): + result = self.send("initialize", {"protocolVersion": "2025-03-26", "capabilities": {}, + "clientInfo": {"name": "compass-paired-evaluation", "version": "1"}}) + if "error" in result: + raise ValueError(f"MCP initialization failed: {result['error']}") + self.send("notifications/initialized", {}, notification=True) + return result diff --git a/benchmarks/agent_query/suite_mcp.json b/benchmarks/agent_query/suite_mcp.json new file mode 100644 index 000000000..33814e20a --- /dev/null +++ b/benchmarks/agent_query/suite_mcp.json @@ -0,0 +1,20 @@ +{ + "schema": "compass.mcp-comparison-inputs/1", + "scope": "Development comparison on the existing five pinned repository graphs. Graph consistency, source correctness, and design quality are separate. Inputs are committed before these MCP questions execute.", + "policy": { + "god_nodes": "Return top 10 hubs. Independently check displayed degree against distinct stored directed endpoint pairs, counting self-loops twice. Ambiguous labels do not verify identity. Report source-backed declarations separately; degree does not prove a god-object defect.", + "get_community": "Return every member of the largest stored community; break size ties by smallest numeric ID. Compare the exact multiset of displayed labels and source files with that tool's graph. Different partitions are allowed. Also query a community ID above the stored maximum and require not found.", + "get_neighbors": "Return calls adjacent to the exact source declaration identified below, using its tool-specific exact ID as input. Compare distinct direction/neighbor/relation triples with stored calls. Separately report repeated-site information and ambiguous labels. ID lookup is preparation, not a measured retrieval success.", + "ambiguous_neighbors": "The same unqualified label denotes multiple source declarations. Require explicit ambiguity or a candidate list; selecting one neighbor list silently fails.", + "graph_stats": "Compare node and community counts with the captured graph, and edge count with stored edge records. Report representation discrepancies rather than treating more edges as better.", + "bounds": "60 seconds per RPC; 16 MiB per response/stderr and 64 MiB per session. For full membership/neighbor enumeration, Graphify receives token_budget=262144; Compass exposes no equivalent option. Record truncation, requested bounds, actual text bytes, and protocol overhead separately. This arm measures complete enumeration, not equal 2000-token answers.", + "source_review": "Review every returned hub identity for source location and inspect its declared role. Review neighbor call directions against the previously reviewed source facts. Functional cluster and god-object judgments remain separate and cannot be inferred from consistency scores." + }, + "repositories": [ + {"name":"cobra", "file":"command.go", "line":757, "symbol":"Find", "sourceText":"func (c *Command) Find(", "ambiguousLabel":null}, + {"name":"flask", "file":"src/flask/app.py", "line":995, "symbol":"full_dispatch_request", "sourceText":"def full_dispatch_request(", "ambiguousLabel":"url_for"}, + {"name":"gson", "file":"gson/src/main/java/com/google/gson/Gson.java", "line":797, "symbol":"newJsonWriter", "sourceText":"public JsonWriter newJsonWriter(", "ambiguousLabel":"toJson"}, + {"name":"zod", "file":"packages/zod/src/v4/classic/from-json-schema.ts", "line":105, "symbol":"detectVersion", "sourceText":"function detectVersion(", "ambiguousLabel":"safeParse"}, + {"name":"axum", "file":"src/routing/path_router.rs", "line":22, "symbol":"validate_path", "sourceText":"fn validate_path(", "ambiguousLabel":"with_state"} + ] +} diff --git a/benchmarks/agent_query/tests/test_mcp_transport.py b/benchmarks/agent_query/tests/test_mcp_transport.py new file mode 100644 index 000000000..a9b1e6bfd --- /dev/null +++ b/benchmarks/agent_query/tests/test_mcp_transport.py @@ -0,0 +1,44 @@ +from pathlib import Path +import sys +import tempfile +import unittest + +from benchmarks.agent_query.mcp_transport import StdioMcp + + +class McpTransportTests(unittest.TestCase): + def invoke(self, body, *, timeout=1, max_bytes=4096): + with tempfile.TemporaryDirectory() as d: + root = Path(d) + script = root / 'server.py' + script.write_text('import sys,json,time\nr=json.loads(sys.stdin.readline())\n'+body) + with StdioMcp([sys.executable, str(script)], root, root/'capture', timeout, max_bytes) as client: + return client.send('tools/list', {}) + + def test_response(self): + r = self.invoke('print(json.dumps({"jsonrpc":"2.0","id":r["id"],"result":{"tools":[]}}),flush=True)') + self.assertEqual(r['result']['tools'], []) + + def test_wrong_id(self): + with self.assertRaisesRegex(ValueError, 'ID'): + self.invoke('print(json.dumps({"jsonrpc":"2.0","id":42,"result":{}}),flush=True)') + + def test_eof(self): + with self.assertRaisesRegex(RuntimeError, 'closed stdout'): + self.invoke('pass') + + def test_deadline(self): + with self.assertRaises(TimeoutError): + self.invoke('time.sleep(5)', timeout=.05) + + def test_output_limit(self): + with self.assertRaisesRegex(ValueError, 'byte bound'): + self.invoke('print("x"*5000,flush=True)', max_bytes=100) + + def test_stderr_limit(self): + with self.assertRaisesRegex(ValueError, 'stderr bound'): + self.invoke('print("x"*5000,file=sys.stderr,flush=True);time.sleep(.2)', max_bytes=100) + + def test_notification_then_response(self): + r = self.invoke('print(json.dumps({"jsonrpc":"2.0","method":"notice"}),flush=True)\nprint(json.dumps({"jsonrpc":"2.0","id":r["id"],"result":{}}),flush=True)') + self.assertEqual(r['result'], {}) From 47767e87d8fc90b9a266005529db7094669abefe Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:19:46 -0700 Subject: [PATCH 12/97] fix: restore MCP communities and reject ambiguous neighbors --- CHANGELOG.md | 6 + COMPATIBILITY.md | 13 ++ MIGRATION.md | 5 + benchmarks/agent_query/COVERAGE_PLAN.md | 5 +- benchmarks/agent_query/README.md | 44 +++++- benchmarks/agent_query/mcp_audit.py | 135 ++++++++++++++++++ benchmarks/agent_query/mcp_compare.py | 14 +- benchmarks/agent_query/mcp_transport.py | 12 +- .../agent_query/tests/test_mcp_audit.py | 54 +++++++ .../agent_query/tests/test_mcp_transport.py | 10 ++ crates/compass-mcp/src/lib.rs | 88 +++++++++++- crates/compass-model/src/document.rs | 46 +++++- ...ode-graph-intelligence-audit-2026-09-26.md | 79 +++++++++- 13 files changed, 493 insertions(+), 18 deletions(-) create mode 100644 benchmarks/agent_query/mcp_audit.py create mode 100644 benchmarks/agent_query/tests/test_mcp_audit.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e3e1da25e..637765be7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Preserve community IDs alongside labels in traversal caches, restoring MCP + community membership and statistics on typed graphs. Older disposable + traversal caches rebuild automatically. +- Return candidate paths and IDs for ambiguous MCP neighbor lookups instead + of silently selecting one declaration. + - Preserve parsed `ask` operands in agent and text answers so headlines, answer evidence, path endpoints, and follow-up actions describe the requested symbols. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index e918d36e9..e10d517ed 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -94,6 +94,19 @@ history profiles, and cache identities. ## Evolving contracts +### MCP community and neighbor lookup + +The disposable traversal cache now retains a labeled community's numeric ID +as well as its name. Cache magic advances from `TRAILT04` to `TRAILT05` so old +projections rebuild from their unchanged graph. MCP membership, statistics, +and other traversal consumers can now observe those stored communities. +Graph schemas and published historical graphs are unchanged. + +MCP `get_neighbors` returns an explicit ambiguity list when multiple nodes +match, ordered by exact ID with at most 20 displayed candidates and an omission +count. Retry with an exact ID to choose a declaration. Exact IDs retain their +case. The tool's input schema and the MCP result envelope are unchanged. + ### Bounded node trails The undirected `path` command also retains nondominated cost/depth states. diff --git a/MIGRATION.md b/MIGRATION.md index d39a96a21..f7651fdae 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,11 @@ layout remains visible and clearly owned. ## Query text and path resolution +MCP callers of `get_neighbors` must handle an `Ambiguous` candidate list and +retry with a returned exact node ID. Earlier versions silently chose one match. +Labeled community IDs are restored by automatic traversal-cache rebuilding; +existing graph files can be queried directly. + `ask` agent output now records parsed operands in `request.operands` and the original question in `request.question`. Consumers needing the question should read that dedicated field. If an existing text cursor fails its prefix check diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 72f1c7e93..f47424b82 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -52,8 +52,9 @@ lacks the optional `mcp` SDK. An isolated `graphifyy[mcp]==0.9.67` environment now provides it; all 227 compared Graphify package files match the original installation. The environment manifest pins its separate dependencies. Actual MCP initialize/tools-list handshakes succeeded for both tools on the retained -Cobra graphs, confirming all four named tools. This is interface availability -evidence; the cross-language community/hub questions still need execution. +Cobra graphs, confirming all four named tools. The subsequent `suite_mcp.json` runs exercise these interfaces across all five +languages. Their graph-consistency results are recorded in the audit report; +source-level cluster quality and god-object design judgments remain pending. Graphify community lookup accepts an explicit token budget; account for that bound and any truncation separately. Lack of one CLI command name does not establish lack of the capability. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 91f8674ee..dc39a270a 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -6,7 +6,8 @@ developer-side tooling: Compass never runs it, and it never installs Graphify. [`COVERAGE_PLAN.md`](COVERAGE_PLAN.md) tracks the broader real-repository question/evidence matrix, including ask, communities, clusters, and god nodes. -Those planned surfaces must not be described as already evaluated. +The audit report distinguishes completed checks from surfaces still awaiting +source or design-quality judgments. Four suites share the harness: @@ -226,3 +227,44 @@ Each subprocess stream is capped at 16 MiB during capture. Exceeding either cap terminates the process group and fails the observation; truncated text is never scored as a successful response. An invalid Compass snapshot pointer fails preparation instead of selecting an arbitrary unpublished snapshot. + +## Shared MCP comparison + +`suite_mcp.json` preregisters 29 questions per tool across the same five-language +panel: graph statistics, top-ten hubs, largest-community enumeration, absent +communities, call neighbors of a reviewed declaration, and four ambiguous +neighbor lookups. These use public MCP tools on both sides. Exact input IDs and +community IDs are prepared symmetrically from retained graphs; this preparation +is not scored as node retrieval. + +```bash +python3 -m benchmarks.agent_query.mcp_compare \ + --run /path/to/captured/five-repository-run \ + --output /path/to/new-mcp-run \ + --compass /path/to/frozen/compass \ + --graphify-python /path/to/isolated-graphify-mcp-env/bin/python \ + --graphify-environment /path/to/graphify-mcp-environment.json +python3 -m benchmarks.agent_query.mcp_audit \ + --run /path/to/new-mcp-run --output /path/to/new-audit.json +``` + +The stdio collector currently requires POSIX pipe selectors (macOS/Linux); it +fails explicitly before starting a server on unsupported platforms. + +The environment manifest records `files` with package-relative `file` and +`mcpEnvironmentSha256` entries plus the installed package/version list. The +collector validates package hashes before and after collection. It records +executable, graph, input, and collector hashes, JSON-RPC transcripts, errors, +text bytes, and timing. The auditor verifies captured answers against those +transcripts and reports actual response-wire bytes separately from text bytes. +The original collector's `protocolBytes` field estimates JSON serialization +size; use the auditor's `wireResponseBytes` for captured transport size. + +Full-enumeration questions use Graphify's large explicit token allowance and +Compass's whole-result interface with common external byte/time limits. This +arm does not claim equal 2,000-token answers. Community membership is compared +with each tool's own partition, not an arbitrary shared cluster number. +Neighbor checks cover displayed direction/label/relation triples and report +ambiguous labels separately. Hub checks recompute displayed degrees for +uniquely identified labels; they do not prove complete ranking eligibility, +source correctness, functional cohesion, or god-object design quality. diff --git a/benchmarks/agent_query/mcp_audit.py b/benchmarks/agent_query/mcp_audit.py new file mode 100644 index 000000000..cf22d7183 --- /dev/null +++ b/benchmarks/agent_query/mcp_audit.py @@ -0,0 +1,135 @@ +"""Independently check captured MCP responses against preregistered graph facts. + +These are graph-consistency diagnostics, not whole-graph source precision or +proof of functional clustering or god-object quality. +""" +from collections import Counter +import argparse +import json +from pathlib import Path +import re + +from benchmarks.agent_query.mcp_compare import community +from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES +from benchmarks.agent_query.runner import _node_anchor, _sha256_file + + +def label(n, tool): + return n.get('name', n.get('label', n['id'])) if tool == 'compass' else n.get('label', n['id']) + + +def audit(row, graph): + tool, kind = row['tool'], row['question'] + result = {'repository':row['repository'],'tool':tool,'question':kind, + 'executionSucceeded':row['executionSucceeded']} + if not row['executionSucceeded']: + return result + text = row['text'] + nodes = {n['id']:n for n in graph['nodes']} + names = {} + for n in nodes.values(): + names.setdefault(label(n,tool),[]).append(n) + communities = {community(n,tool) for n in nodes.values()} - {None} + if kind == 'stats': + actual = {k:int(v) for k,v in re.findall(r'^(Nodes|Edges|Communities): (\d+)$',text,re.M)} + expected = {'Nodes':len(nodes),'Edges':len(graph['links']),'Communities':len(communities)} + result.update(expected=expected,actual=actual,graphCountsMatch=actual==expected) + elif kind == 'community': + cid = row['arguments']['community_id'] + members = [n for n in nodes.values() if community(n,tool)==cid] + expected = Counter((label(n,tool),_node_anchor(n,tool)[0] or '') for n in members) + actual = Counter(re.findall(r'^ (.*) \[(.*)\]$',text,re.M)) + header = re.match(r'Community \d+.* \((\d+) nodes\):',text) + result.update(expectedMembers=len(members),returnedMembers=sum(actual.values()), + missingMembers=sum((expected-actual).values()),extraMembers=sum((actual-expected).values()), + membershipMatches=actual==expected and header is not None and int(header[1])==len(members)) + elif kind == 'missing-community': + cid=row['arguments']['community_id'] + result['absenceMatches'] = cid not in communities and text==f'Community {cid} not found.' + elif kind == 'ambiguous-neighbors': + result['ambiguityPreserved'] = 'ambig' in text.lower() and not text.startswith('Neighbors of ') + elif kind == 'hubs': + degree=Counter() + for a,b in {(e['source'],e['target']) for e in graph['links']}: + degree[a]+=1;degree[b]+=1 + hubs=[] + for rank,name,count in re.findall(r'^ (\d+)\. (.*) - (\d+) edges$',text,re.M): + matches=names.get(name,[]) + entry={'rank':int(rank),'label':name,'degree':int(count),'identityCandidates':len(matches)} + if len(matches)==1: + n=matches[0];file,line,_=_node_anchor(n,tool) + entry.update(id=n['id'],file=file,line=line,expectedDegree=degree[n['id']],degreeMatches=degree[n['id']]==int(count)) + hubs.append(entry) + result.update(hubs=hubs,returned=len(hubs),verifiedIdentities=sum(x['identityCandidates']==1 for x in hubs), + matchingDegrees=sum(x.get('degreeMatches',False) for x in hubs)) + elif kind == 'neighbors': + seed=row['arguments']['label'] + actual=set(re.findall(r'^ (-->|<--) (.*?) \[([^\]]*)\] \[[^\]]*\]',text,re.M)) + expected=set();semantic=set() + for e in graph['links']: + rel=e.get('kind' if tool=='compass' else 'relation','') + if rel!='calls':continue + a,b=e['source'],e['target'] + if a==seed:expected.add(('-->',label(nodes[b],tool),rel)) + if b==seed:expected.add(('<--',label(nodes[a],tool),rel)) + a,b=(e.get('_src',a),e.get('_tgt',b)) if tool=='graphify' else (a,b) + if a==seed:semantic.add(('-->',label(nodes[b],tool),rel)) + if b==seed:semantic.add(('<--',label(nodes[a],tool),rel)) + result.update(expectedDisplayedTriples=len(expected),returnedTriples=len(actual), + missing=sorted(expected-actual),extra=sorted(actual-expected),displayedPairsMatch=actual==expected, + semanticDirectionsMatch=actual==semantic, + ambiguousNeighborLabels=sorted({name for _,name,_ in actual if len(names.get(name,[]))!=1})) + return result + + +def main(args): + run=json.loads(read_bounded(args.run/'run.json')) + if not run.get('complete'):raise ValueError('capture is incomplete') + old=Path(run['sourceRun']) + if _sha256_file(old/'run.json')!=run['sourceRunSha256']:raise ValueError('source run changed') + source=json.loads(read_bounded(old/'run.json')) + if _sha256_file(args.run/'inputs.json')!=run['inputSha256']: + raise ValueError('input manifest digest mismatch') + if _sha256_file(args.run/'mcp_compare.py')!=run['collectorSha256']: + raise ValueError('collector digest mismatch') + if 'transportSha256' in run and _sha256_file(args.run/'mcp_transport.py')!=run['transportSha256']: + raise ValueError('transport digest mismatch') + results=[] + graphs={} + for row in run['results']: + key=(row['repository'],row['tool']) + if key[0] not in {'cobra','flask','gson','zod','axum'} or key[1] not in {'compass','graphify'}: + raise ValueError('invalid capture key') + if key not in graphs: + repo=next(r for r in source['repositories'] if r['repository']==key[0]) + p=Path(repo[key[1]+'Graph']) + if _sha256_file(p)!=row['graphSha256']:raise ValueError('graph digest mismatch') + graphs[key]=json.loads(read_bounded(p,MAX_GRAPH_BYTES)) + if row['executionSucceeded']: + packet=row['response'] + identity=packet.get('id') + if type(identity) is not int or not 1 <= identity <= 100: + raise ValueError('invalid captured response ID') + raw=read_bounded(args.run/'raw'/key[0]/key[1]/f'{identity:02}.response.jsonl') + packets=[json.loads(line) for line in raw.splitlines() if line] + if not any(p==packet for p in packets): + raise ValueError('response differs from raw transcript') + text='\n'.join(c['text'] for c in packet.get('result',{}).get('content',[]) if c.get('type')=='text') + if text!=row['text'] or len(text.encode())!=row['textBytes']: + raise ValueError('answer text differs from response') + checked=audit(row,graphs[key]) + if row['executionSucceeded']: + checked['wireResponseBytes']=len(raw) + checked['textBytes']=row['textBytes'] + results.append(checked) + report={'scope':__doc__,'captureSha256':_sha256_file(args.run/'run.json'), + 'auditorSha256':_sha256_file(Path(__file__)),'results':results} + with args.output.open('x') as f:json.dump(report,f,indent=2,sort_keys=True) + for r in results: + print(r['repository'],r['tool'],r['question'],{k:v for k,v in r.items() if k.endswith('Match') or k.endswith('Matches') or k.endswith('Preserved')}) + + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--run',type=Path,required=True);p.add_argument('--output',type=Path,required=True) + main(p.parse_args()) diff --git a/benchmarks/agent_query/mcp_compare.py b/benchmarks/agent_query/mcp_compare.py index 03b3ed445..cc4f19950 100644 --- a/benchmarks/agent_query/mcp_compare.py +++ b/benchmarks/agent_query/mcp_compare.py @@ -44,6 +44,14 @@ def prepare_questions(graph, tool, witness): return queries +def skipped_results(repository, tool, questions, graph_digest, argv): + return [{'repository':repository, 'tool':tool, 'question':identifier, + 'method':method, 'arguments':params, 'graphSha256':graph_digest, + 'argv':argv, 'executionSucceeded':False, + 'captureError':'not executed after the MCP connection failed'} + for identifier,method,params in questions] + + def verify_environment(args): manifest = json.loads(read_bounded(args.graphify_environment)) roots = list(args.graphify_python.parent.parent.glob('lib/python*/site-packages/graphify')) @@ -73,7 +81,8 @@ def execute(args): shutil.copy2(Path(__file__).with_name(name), args.output/name) report = {'schema':'compass.mcp-comparison-capture/1', 'sourceRunSha256':_sha256_file(args.run/'run.json'), 'inputSha256':_sha256_file(args.inputs), 'sourceRun':str(args.run.resolve()), - 'collectorSha256':_sha256_file(Path(__file__)), 'servers':{}, 'results':[]} + 'collectorSha256':_sha256_file(Path(__file__)), + 'transportSha256':_sha256_file(Path(__file__).with_name('mcp_transport.py')), 'servers':{}, 'results':[]} for tool, binary in [('compass',args.compass),('graphify',args.graphify_python)]: report['servers'][tool] = {'executable':str(binary.resolve()), 'executableSha256':_sha256_file(binary)} for witness in manifest['repositories']: @@ -99,7 +108,7 @@ def execute(args): session.initialize() listing = session.send('tools/list',{}) advertised = {t['name'] for t in listing.get('result',{}).get('tools',[])} - for identifier, method, params in questions: + for position, (identifier, method, params) in enumerate(questions): if method not in advertised: raise ValueError(f'{tool} did not advertise {method}') started = time.monotonic() @@ -119,6 +128,7 @@ def execute(args): (args.output/'run.json').write_text(json.dumps(report,indent=2,sort_keys=True)+'\n') print(name,tool,identifier,row['executionSucceeded'],row.get('textBytes'),flush=True) if 'captureError' in row: + report['results'].extend(skipped_results(name,tool,questions[position+1:],digest,argv)) break # A desynchronized connection cannot be reused. if _sha256_file(graph_path) != digest: raise ValueError('graph changed during MCP questions') diff --git a/benchmarks/agent_query/mcp_transport.py b/benchmarks/agent_query/mcp_transport.py index 51b747888..b9541c561 100644 --- a/benchmarks/agent_query/mcp_transport.py +++ b/benchmarks/agent_query/mcp_transport.py @@ -22,6 +22,8 @@ def __init__(self, argv: list[str], cwd: Path, directory: Path, self.buffer = b"" def __enter__(self): + if os.name != "posix": + raise OSError("MCP comparison transport requires POSIX pipe selectors") self.directory.mkdir(parents=True, exist_ok=False) self.process = subprocess.Popen(self.argv, cwd=self.cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -39,11 +41,17 @@ def __exit__(self, *_): try: self.process.wait(timeout=1) except subprocess.TimeoutExpired: - os.killpg(self.process.pid, signal.SIGTERM) + try: + os.killpg(self.process.pid, signal.SIGTERM) + except ProcessLookupError: + pass try: self.process.wait(timeout=1) except subprocess.TimeoutExpired: - os.killpg(self.process.pid, signal.SIGKILL) + try: + os.killpg(self.process.pid, signal.SIGKILL) + except ProcessLookupError: + pass self.process.wait(timeout=2) self.selector.close() self.error_file.close() diff --git a/benchmarks/agent_query/tests/test_mcp_audit.py b/benchmarks/agent_query/tests/test_mcp_audit.py new file mode 100644 index 000000000..46ecd0bb6 --- /dev/null +++ b/benchmarks/agent_query/tests/test_mcp_audit.py @@ -0,0 +1,54 @@ +import unittest +from benchmarks.agent_query.mcp_audit import audit + + +class McpAuditTests(unittest.TestCase): + def test_connection_failure_does_not_remove_remaining_questions(self): + from benchmarks.agent_query.mcp_compare import skipped_results + questions=[('hubs','god_nodes',{'top_n':10}),('community','get_community',{'community_id':0})] + rows=skipped_results('cobra','compass',questions,'digest',['compass']) + self.assertEqual([r['question'] for r in rows],['hubs','community']) + self.assertTrue(all(not r['executionSucceeded'] and r['captureError'] for r in rows)) + + def setUp(self): + self.graph={'nodes':[{'id':'a','name':'Alpha','community':{'id':2},'source':{'file':'a.rs','startLine':1}}, + {'id':'b','name':'Beta','community':{'id':2},'source':{'file':'b.rs','startLine':1}}], + 'links':[{'source':'a','target':'b','kind':'calls'}]} + + def row(self,kind,text,arguments=None): + return {'repository':'fixture','tool':'compass','question':kind,'text':text, + 'arguments':arguments or {},'executionSucceeded':True} + + def test_missing_real_community_fails(self): + r=audit(self.row('community','Community 2 not found.',{'community_id':2}),self.graph) + self.assertFalse(r['membershipMatches']);self.assertEqual(r['missingMembers'],2) + + def test_membership_multiset_and_header(self): + text='Community 2 — Core (2 nodes):\n Alpha [a.rs]\n Beta [b.rs]' + self.assertTrue(audit(self.row('community',text,{'community_id':2}),self.graph)['membershipMatches']) + self.assertFalse(audit(self.row('community',text.replace('Beta [b.rs]','Alpha [a.rs]'),{'community_id':2}),self.graph)['membershipMatches']) + + def test_unambiguous_display_does_not_prove_overloaded_identity(self): + self.graph['nodes'][1]['name']='Alpha' + r=audit(self.row('hubs','God nodes (most connected):\n 1. Alpha - 1 edges'),self.graph) + self.assertEqual(r['verifiedIdentities'],0) + self.assertEqual(r['matchingDegrees'],0) + + def test_degree_collapses_parallel_pairs_and_counts_self_loop_twice(self): + self.graph['links'] += [dict(self.graph['links'][0]),{'source':'a','target':'a','kind':'calls'}] + r=audit(self.row('hubs','God nodes (most connected):\n 1. Alpha - 3 edges'),self.graph) + self.assertEqual(r['matchingDegrees'],1) + + def test_silent_selection_is_not_ambiguity(self): + r=audit(self.row('ambiguous-neighbors','Neighbors of ambiguous_name:\n --> Alpha [calls] [EXTRACTED]'),self.graph) + self.assertFalse(r['ambiguityPreserved']) + self.assertTrue(audit(self.row('ambiguous-neighbors','Ambiguous: choose an ID'),self.graph)['ambiguityPreserved']) + + def test_neighbor_direction_and_missing_relationship(self): + args={'label':'a'} + self.assertTrue(audit(self.row('neighbors','Neighbors of Alpha:\n --> Beta [calls] [EXTRACTED]',args),self.graph)['displayedPairsMatch']) + self.assertFalse(audit(self.row('neighbors','Neighbors of Alpha:\n <-- Beta [calls] [EXTRACTED]',args),self.graph)['displayedPairsMatch']) + + def test_stats_dont_accept_missing_communities(self): + r=audit(self.row('stats','Nodes: 2\nEdges: 1\nCommunities: 0'),self.graph) + self.assertFalse(r['graphCountsMatch']) diff --git a/benchmarks/agent_query/tests/test_mcp_transport.py b/benchmarks/agent_query/tests/test_mcp_transport.py index a9b1e6bfd..5e23e454b 100644 --- a/benchmarks/agent_query/tests/test_mcp_transport.py +++ b/benchmarks/agent_query/tests/test_mcp_transport.py @@ -2,11 +2,21 @@ import sys import tempfile import unittest +from unittest.mock import patch from benchmarks.agent_query.mcp_transport import StdioMcp class McpTransportTests(unittest.TestCase): + def test_unsupported_platform_fails_before_starting_a_process(self): + with tempfile.TemporaryDirectory() as d: + root = Path(d) + client = StdioMcp([sys.executable], root, root/'capture') + with patch('benchmarks.agent_query.mcp_transport.os.name', 'nt'): + with self.assertRaisesRegex(OSError, 'POSIX'): + client.__enter__() + self.assertFalse((root/'capture').exists()) + def invoke(self, body, *, timeout=1, max_bytes=4096): with tempfile.TemporaryDirectory() as d: root = Path(d) diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 6d656090b..5200e26c4 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -2209,13 +2209,42 @@ fn tool_get_neighbors( arguments: &Map, context: &GraphContext, ) -> Result { - let query = string_argument(arguments, "label")?.to_lowercase(); + let query = string_argument(arguments, "label")?; let filter = optional_string(arguments, "relation_filter") .unwrap_or_default() .to_lowercase(); - let Some(&index) = find_node(&context.graph, &query).first() else { + let matches = find_node(&context.graph, query); + let Some(&index) = matches.first() else { return Ok(format!("No node matching '{query}' found.")); }; + if matches.len() > 1 { + const MAX_CANDIDATES: usize = 20; + let mut candidates = matches + .iter() + .map(|index| context.graph.node(*index)) + .collect::>(); + candidates.sort_by(|left, right| left.id.cmp(&right.id)); + let mut lines = vec![format!( + "Ambiguous: '{}' matches {} nodes. Retry with an exact node ID.", + sanitize_label(query), + candidates.len() + )]; + lines.extend(candidates.iter().take(MAX_CANDIDATES).map(|node| { + format!( + " {} [{}] id: {}", + sanitize_label(node.label()), + sanitize_label(&node.string("source_file")), + sanitize_label(&node.id) + ) + })); + if candidates.len() > MAX_CANDIDATES { + lines.push(format!( + "{} additional candidates omitted; narrow the symbol or source path.", + candidates.len() - MAX_CANDIDATES + )); + } + return Ok(lines.join("\n")); + } let mut lines = vec![format!( "Neighbors of {}:", sanitize_label(context.graph.node(index).label()) @@ -2855,6 +2884,61 @@ fn read_bounded_resource(path: &Path) -> Result { mod tests { use super::*; + #[test] + fn mcp_reports_stored_typed_communities() -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("graph.json"); + fs::write( + &path, + r#"{"nodes":[ + {"id":"a","name":"Alpha","kind":"function","community":{"id":2,"label":"Core"},"source":{"file":"a.rs","startLine":1}}, + {"id":"b","name":"Beta","kind":"function","community":{"id":2,"label":"Core"},"source":{"file":"b.rs","startLine":1}}],"links":[]}"#, + )?; + let server = CompassMcp::new(&path); + assert!( + server + .invoke("graph_stats", Map::new()) + .contains("Communities: 1") + ); + let members = server.invoke( + "get_community", + json!({"community_id":2}).as_object().ok_or("args")?.clone(), + ); + assert!(members.contains("Core (2 nodes)"), "{members}"); + assert!(members.contains("Alpha [a.rs]")); + assert!(members.contains("Beta [b.rs]")); + Ok(()) + } + + #[test] + fn mcp_neighbors_require_unique_identity() -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("graph.json"); + fs::write( + &path, + r#"{"directed":true,"nodes":[ + {"id":"A","label":"run()","qualifiedName":"First.run","source_file":"first.rs"}, + {"id":"b","label":"run()","qualifiedName":"Second.run","source_file":"second.rs"}, + {"id":"c","label":"OnlyFirst"}],"links":[{"source":"A","target":"c","relation":"calls"}]}"#, + )?; + let server = CompassMcp::new(&path); + let ambiguous = server.invoke( + "get_neighbors", + json!({"label":"run"}).as_object().ok_or("args")?.clone(), + ); + assert!(ambiguous.contains("Ambiguous"), "{ambiguous}"); + assert!(!ambiguous.contains("-->")); + assert!(ambiguous.contains("first.rs") && ambiguous.contains("second.rs")); + for exact in ["A", "First.run"] { + let output = server.invoke( + "get_neighbors", + json!({"label":exact}).as_object().ok_or("args")?.clone(), + ); + assert!(output.contains("--> OnlyFirst"), "{exact}: {output}"); + } + Ok(()) + } + #[test] fn invocation_errors_preserve_json_rpc_taxonomy() { assert_eq!( diff --git a/crates/compass-model/src/document.rs b/crates/compass-model/src/document.rs index bd7210d43..8d1673689 100644 --- a/crates/compass-model/src/document.rs +++ b/crates/compass-model/src/document.rs @@ -959,7 +959,7 @@ impl GraphDocument { const QUERY_CACHE_MAGIC: &[u8; 8] = b"TRAILG01"; const AFFECTED_CACHE_MAGIC: &[u8; 8] = b"TRAILA02"; -const TRAVERSAL_CACHE_MAGIC: &[u8; 8] = b"TRAILT04"; +const TRAVERSAL_CACHE_MAGIC: &[u8; 8] = b"TRAILT05"; const QUERY_CACHE_HEADER_LEN: usize = 28; static QUERY_CACHE_SEQUENCE: AtomicU64 = AtomicU64::new(0); @@ -1388,12 +1388,11 @@ impl TraversalRawNode { .map(|label| Value::String(label.to_owned())) }) .or(community_name); - let community = community.filter(|value| { - value - .as_object() - .and_then(|community| community.get("label")) - .and_then(Value::as_str) - .is_none() + // Compact labeled communities to their identity rather than dropping + // the whole record after extracting the display name. + let community = community.and_then(|value| match value { + Value::Object(mut fields) => fields.remove("id"), + scalar => Some(scalar), }); let label = label .filter(|value| value_as_python_string(value).is_some()) @@ -1890,6 +1889,39 @@ mod tests { GraphDocument, NodeRecord, affected_cache_path, query_cache_path, traversal_cache_path, }; + #[test] + fn traversal_cache_preserves_labeled_community_ids() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + fs::create_dir(directory.path().join("cache"))?; + fs::write( + &path, + r#"{"nodes":[ + {"id":"a","name":"A","community":{"id":4,"label":"Core"}}, + {"id":"b","name":"B","community":{"id":7}}, + {"id":"c","label":"C","community":9}],"links":[]}"#, + )?; + for _ in 0..2 { + let graph = GraphDocument::load_for_traversal(&path)?; + assert_eq!(graph.nodes[0].unsigned("community"), Some(4)); + assert_eq!(graph.nodes[0].string("community_name"), "Core"); + assert_eq!(graph.nodes[1].unsigned("community"), Some(7)); + assert_eq!(graph.nodes[2].unsigned("community"), Some(9)); + } + // Old disposable caches can contain the label but lack its ID. + let mut stale = super::load_traversal_projection(&path)?.into_cache(); + stale.2[0].8 = None; + let signature = super::graph_signature(&path).ok_or("missing graph signature")?; + super::write_traversal_cache(&path, signature, &stale)?; + let cache = traversal_cache_path(&path); + let mut bytes = fs::read(&cache)?; + bytes[..8].copy_from_slice(b"TRAILT04"); + fs::write(cache, bytes)?; + let graph = GraphDocument::load_for_traversal(&path)?; + assert_eq!(graph.nodes[0].unsigned("community"), Some(4)); + Ok(()) + } + #[test] fn omitted_multigraph_uses_networkx_legacy_default() { let document: GraphDocument = serde_json::from_str(r#"{"nodes":[{"id":"a"}],"links":[]}"#) diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 62894c18d..ddb7ac455 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -522,13 +522,88 @@ debug executable during concurrent qualification; timings are not speed comparisons. The new ask failures demonstrate why that earlier suite is insufficient to establish the requested superiority. +## Shared MCP comparison across five languages + +Commit `152efdd7` records `suite_mcp.json` and the bounded stdio collector before +execution. There are 29 questions per tool on the retained five-repository +panel: graph counts, top-ten hubs, largest-community enumeration, missing +communities, calls adjacent to reviewed declarations, and four ambiguous +neighbor names. Both products expose the same MCP operations. Input node IDs +and community IDs are prepared symmetrically from the graphs and are not +counted as successful node retrieval. Sources and graph hashes are checked +before and after use. + +Graphify runs in the isolated 0.9.67 MCP environment described in the coverage +plan. Its optional SDK dependencies are separately recorded, and all 227 +compared Graphify package files match the original installation. This avoids +counting an absent optional SDK as a product failure. Full-enumeration requests +use its explicit 262,144-token allowance; Compass exposes whole-result output. +The external limits are 60 seconds per RPC, 16 MiB per response/stderr, and +64 MiB per session. These are complete-enumeration tasks, not an equal +2,000-token arm. Text bytes and actual response-wire bytes are reported +separately. Concurrent debug timings are not performance comparisons. + +All 58 RPCs completed in `mcp-paired-01`. The original run exposed two production +defects: the compact traversal projection retained community names but dropped +labeled community IDs, and `get_neighbors` selected the first ambiguous match. +The independent auditor checks source-run/graph/input/collector hashes and +captured responses against raw JSON-RPC transcripts. Its checks implement the +preregistered graph-consistency policies; these are not complete source-precision +or functional-clustering oracles. + +| Graph-consistency check | Initial Compass | Corrected Compass | Graphify in both runs | +| --- | ---: | ---: | ---: | +| Node/edge/community totals | 0/5 | 5/5 | 5/5 | +| Largest-community member multiset and count | 0/5 | 5/5 | 5/5 | +| Absent community is reported absent | 5/5 | 5/5 | 5/5 | +| Filtered neighbor direction/label/relation triples | 5/5 | 5/5 | 5/5 | +| Ambiguous neighbor lookup preserves ambiguity | 0/4 | 4/4 | 4/4 | + +The first two failures share one cache defect; they are not ten independent +implementation bugs. Native regressions failed before both corrections. +The model now retains the community ID and display name in its compact cache, +and advances cache magic to `TRAILT05` so previously deficient `TRAILT04` +projections rebuild. Cold, warm, and stale-cache regression cases pass. +MCP now returns a stable candidate list (at most 20 plus an omission count) +with source paths and exact IDs. Exact IDs preserve case. Graph schemas and +historical artifacts remain unchanged. + +Replay `mcp-paired-02` uses the same questions, source graphs, and Graphify +environment. It rebuilds the old Compass traversal caches automatically and +passes the graph-consistency checks above. The corrected debug binary SHA256 +is `4b3b755fd8a3e8d74c4eac8c336f719506dcfee9f8ac89cea2eafbe181042efb`; +source/file hashes are under `mcp-corrections-provenance`. No oracle was retuned +to replace the initial failures. Reports and transcripts for both runs remain. + +The hub outputs expose another unresolved limitation. Only 29/50 Compass and +37/50 Graphify entries have labels that uniquely identify a graph node. +All of those identifiable entries have matching independently computed degree; +the remaining entries are unverified because their labels collide. Matching a +label plus its expected degree to choose a convenient identity would conceal +this limitation. Both MCP outputs need stronger identity presentation for +reliable navigation. These counts do not verify ranking eligibility or source +correctness, and high degree does not establish excessive responsibility or +poor cohesion. Zod's highly connected module nodes also need explicit role +interpretation before any design conclusion. + +Verification after the two corrections: 1,148 native tests passed (1,086 +workspace lib/bin tests plus 36 CLI query, 9 product, and 17 output tests), +zero failed, two ignored. Workspace Clippy passes with warnings denied. +The benchmark/transport/auditor suite passes 65 Python tests, including +membership mismatches, ambiguous labels, direction errors, parallel edges, +self-loops, transport timeouts/byte limits, and unsupported-platform handling. +Formatting, diff checks, and the native product boundary pass. Full extraction +fixture qualification remains the preceding receiver checkpoint; this change +is verified through native cache/MCP regressions and the retained-graph replay. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. -2. Expand hub review beyond candidate eligibility to source-reviewed design - judgments, separating connectivity from responsibility/cohesion defects. +2. Add unambiguous hub identities and source anchors, then expand hub review + to source-reviewed design judgments. Evaluate cluster responsibilities and + cross-community connections separately from graph consistency. 3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, traversal direction, source occurrences, ambiguity, unreachable nodes, and bound exhaustion. A negative or limit outcome must never count as a path. From 7b2430eb13a3ddc5f8601c80fec6058c9301b309 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:28:39 -0700 Subject: [PATCH 13/97] fix: filter MCP relations before grouping neighbors --- CHANGELOG.md | 2 + COMPATIBILITY.md | 4 ++ crates/compass-mcp/src/lib.rs | 49 ++++++++++++++++--- ...ode-graph-intelligence-audit-2026-09-26.md | 29 +++++++++++ 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 637765be7..f44292ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ traversal caches rebuild automatically. - Return candidate paths and IDs for ambiguous MCP neighbor lookups instead of silently selecting one declaration. +- Apply MCP neighbor relationship filters before grouping repeated neighbors, + preserving matching calls when another relation shares the same endpoints. - Preserve parsed `ask` operands in agent and text answers so headlines, answer evidence, path endpoints, and follow-up actions describe the requested diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index e10d517ed..d47b13537 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -106,6 +106,10 @@ MCP `get_neighbors` returns an explicit ambiguity list when multiple nodes match, ordered by exact ID with at most 20 displayed candidates and an omission count. Retry with an exact ID to choose a declaration. Exact IDs retain their case. The tool's input schema and the MCP result envelope are unchanged. +Relationship filters apply before repeated neighbors are grouped, so a stored +call remains visible when a containment/reference edge precedes it. The tool +continues to return distinct neighbors; typed call-query tools carry occurrence +and source-site detail. ### Bounded node trails diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 5200e26c4..16d13fc99 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -2255,13 +2255,13 @@ fn tool_get_neighbors( let Some(neighbor) = context.graph.node_index(&edge.target) else { continue; }; - if !outgoing.insert(neighbor) { - continue; - } let relation = edge.string("relation"); if !filter.is_empty() && !relation.to_lowercase().contains(&filter) { continue; } + if !outgoing.insert(neighbor) { + continue; + } lines.push(format!( " --> {} [{}] [{}]", sanitize_label(context.graph.node(neighbor).label()), @@ -2275,13 +2275,13 @@ fn tool_get_neighbors( let Some(neighbor) = context.graph.node_index(&edge.source) else { continue; }; - if !incoming.insert(neighbor) { - continue; - } let relation = edge.string("relation"); if !filter.is_empty() && !relation.to_lowercase().contains(&filter) { continue; } + if !incoming.insert(neighbor) { + continue; + } lines.push(format!( " <-- {} [{}] [{}]", sanitize_label(context.graph.node(neighbor).label()), @@ -2884,6 +2884,43 @@ fn read_bounded_resource(path: &Path) -> Result { mod tests { use super::*; + #[test] + fn mcp_neighbor_filter_precedes_neighbor_grouping() -> Result<(), Box> { + let temp = tempfile::tempdir()?; + for reverse in [false, true] { + let path = temp.path().join(format!("graph-{reverse}.json")); + let mut links = vec![ + json!({"source":"a","target":"b","relation":"contains"}), + json!({"source":"a","target":"b","relation":"calls"}), + json!({"source":"a","target":"b","relation":"calls"}), + ]; + if reverse { + links.reverse(); + } + fs::write( + &path, + serde_json::to_vec(&json!({ + "directed":true,"multigraph":true, + "nodes":[{"id":"a","label":"Alpha"},{"id":"b","label":"Beta"}], + "links":links + }))?, + )?; + let server = CompassMcp::new(&path); + for (id, expected) in [("a", "--> Beta [calls]"), ("b", "<-- Alpha [calls]")] { + let output = server.invoke( + "get_neighbors", + json!({"label":id,"relation_filter":"calls"}) + .as_object() + .ok_or("args")? + .clone(), + ); + assert_eq!(output.matches(expected).count(), 1, "{output}"); + assert!(!output.contains("[contains]")); + } + } + Ok(()) + } + #[test] fn mcp_reports_stored_typed_communities() -> Result<(), Box> { let temp = tempfile::tempdir()?; diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index ddb7ac455..638476248 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -596,6 +596,35 @@ Formatting, diff checks, and the native product boundary pass. Full extraction fixture qualification remains the preceding receiver checkpoint; this change is verified through native cache/MCP regressions and the retained-graph replay. +### Neighbor filtering diagnostic + +The five selected neighbor questions missed another defect: MCP grouped each +neighbor before applying the relation filter. A preceding containment or +reference edge could therefore hide a later call between the same endpoints. +Both incoming and outgoing lookups were affected. A post-output source review +of Flask `tests/test_helpers.py:275–281` establishes the nested `index` to +`generate` call. The retained graph contains both containment and call edges, +but both filtered MCP lookups returned empty adjacency before correction. +This diagnostic is separate from the preregistered comparison scores. + +The filter now precedes grouping. Native regression coverage exercises both +directions, both edge orders, and duplicate calls. The tool continues to return +distinct neighbors; it does not promise call-site multiplicity. The unchanged +Flask source and graph now produce the correct outgoing and incoming call in +`mcp-filter-diagnostic-02`, with original failures retained in +`mcp-filter-diagnostic-01`. Similar edge-order candidates exist elsewhere in +the panel, but their presence alone is not source-accuracy evidence. + +The final filter executable is retained under `mcp-filter-provenance`, SHA256 +`367dfd78301d9c3914049b8c134933dce91934003eafbc2c144d9abd760a7e1a`. +Native verification passes 1,149 tests, zero failed, two ignored; workspace +Clippy passes with warnings denied. Formatting, diff checks, and the product +boundary also pass. This is a query correction on retained graphs; no new +extraction or performance claim follows. Replay `mcp-paired-03` completes all +58 RPCs; `mcp-audit-03.json` confirms the same graph-consistency results as +`mcp-paired-02` for both tools. The separate Flask diagnostic is the evidence +for the additional filter correction. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact From 7c554c2f117eb8fc515dd386583197c8926746c7 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:36:54 -0700 Subject: [PATCH 14/97] bench: define identity-based hub navigation diagnostic --- benchmarks/agent_query/COVERAGE_PLAN.md | 20 +++ benchmarks/agent_query/hub_navigation.py | 156 ++++++++++++++++++ benchmarks/agent_query/mcp_audit.py | 30 +++- .../agent_query/tests/test_hub_navigation.py | 31 ++++ .../agent_query/tests/test_mcp_audit.py | 23 +++ 5 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 benchmarks/agent_query/hub_navigation.py create mode 100644 benchmarks/agent_query/tests/test_hub_navigation.py diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index f47424b82..170d73fdf 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -73,6 +73,26 @@ Do not compare one tool's model-assisted output with the other's native output. ## Scoring and acceptance +### Hub navigation diagnostic + +The label-identity gap found in the first MCP run motivates a separate +development diagnostic. Preserve that run and its label-only results. For +each of the ten hubs returned on each of the five repositories, issue one +`get_neighbors` follow-up using only the returned exact ID when available, +otherwise its returned label. Do not read a graph to substitute an ID for +either product. Both products get the same one-follow-up allowance and the +existing full-enumeration bounds. Explicit ambiguity is safe but does not +complete direct navigation. This diagnostic does not measure workflows with +additional disambiguation steps or Graphify's separate CLI JSON hub workflow. + +The independent oracle may use the captured graph to verify exact identity, +degree, source anchors, and the multiset of displayed direction/neighbor-label +pairs after grouping by distinct neighbor. It must not resolve identity using +the expected degree. Unfiltered neighbors may select one relation per neighbor; +this diagnostic does not score complete parallel-relation or occurrence recall. +Capture every response, failure, and output byte. Source-based design quality, +complete top-N eligibility, and ranking correctness remain separate questions. + - Publish category-level results and every failure, including competitor wins. - Keep source correctness, graph consistency, task availability, and output efficiency separate. No single combined score may hide a precision failure. diff --git a/benchmarks/agent_query/hub_navigation.py b/benchmarks/agent_query/hub_navigation.py new file mode 100644 index 000000000..3452df37a --- /dev/null +++ b/benchmarks/agent_query/hub_navigation.py @@ -0,0 +1,156 @@ +"""One-follow-up hub navigation diagnostic; selectors come only from tool output. + +This development diagnostic checks displayed adjacency against stored graphs, +not source precision or god-object design quality. See COVERAGE_PLAN.md. +""" +import argparse +from collections import Counter +import json +from pathlib import Path +import re +import shutil +import time +from types import SimpleNamespace + +from benchmarks.agent_query.mcp_audit import audit, label, main as audit_capture +from benchmarks.agent_query.mcp_compare import verify_environment +from benchmarks.agent_query.mcp_transport import StdioMcp +from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES +from benchmarks.agent_query.runner import _sha256_file, _verify_source, load_suite + + +def selectors(row): + """Never consult a graph or inferred degree to choose a selector.""" + structured = row['response'].get('result', {}).get('structuredContent') + if structured is not None: + if structured.get('schema') != 'compass.mcp.tool-result/1' or structured.get('result', {}).get('schema') != 'compass.mcp.hubs/1': + raise ValueError('unsupported structured hub response') + return [(n['rank'], n['id'], 'explicit-id') for n in structured['result']['nodes']] + return [(int(rank), name, 'display-label') for rank, name in + re.findall(r'^ (\d+)\. (.*) - \d+ edges$', row['text'], re.M)] + + +def check_navigation(text, graph, tool, seed): + """Seed is oracle identity, never a substituted request selector.""" + nodes = {n['id']: n for n in graph['nodes']} + if seed not in nodes: + return {'identityKnown': False, 'navigationResolved': False, + 'ambiguityReported': 'ambig' in text.lower()} + expected = set() + for edge in graph['links']: + a, b = edge['source'], edge['target'] + if a == seed: + expected.add(('-->', b)) + if b == seed: + expected.add(('<--', a)) + expected_labels = Counter((direction, label(nodes[n], tool)) for direction, n in expected) + actual = Counter(re.findall(r'^ (-->|<--) (.*?) \[[^\]]*\] \[[^\]]*\]', text, re.M)) + header = text.splitlines()[0] if text else '' + header_matches = header == f'Neighbors of {label(nodes[seed], tool)}:' + return {'identityKnown': True, 'seed': seed, + 'expectedDisplayedNeighbors': sum(expected_labels.values()), + 'returnedDisplayedNeighbors': sum(actual.values()), + 'displayedAdjacencyMatches': expected_labels == actual, + 'navigationResolved': header_matches and expected_labels == actual} + + +def main(args): + args.output.mkdir(parents=True, exist_ok=False) + # Verify the source capture, graphs, and complete raw RPC transcripts first. + audit_capture(SimpleNamespace(run=args.run, output=args.output/'input-audit.json')) + run = json.loads(read_bounded(args.run/'run.json')) + source_run = Path(run['sourceRun']) + source = json.loads(read_bounded(source_run/'run.json')) + suite = load_suite(source_run/'suite.toml') + if suite.digest != source['suiteDigest']: + raise ValueError('source suite changed') + environment = SimpleNamespace( + graphify_python=Path(run['servers']['graphify']['executable']), + graphify_environment=args.run/'graphify-environment.json') + verify_environment(environment) + for record in run['servers'].values(): + if _sha256_file(Path(record['executable'])) != record['executableSha256']: + raise ValueError('captured server executable changed') + shutil.copy2(__file__, args.output/'hub_navigation.py') + report = {'scope': __doc__, 'inputRun': str(args.run.resolve()), + 'inputRunSha256': _sha256_file(args.run/'run.json'), + 'collectorSha256': _sha256_file(Path(__file__)), 'results': [], 'complete': False} + report['supportFiles'] = {} + for name in ['mcp_audit.py', 'mcp_transport.py', 'mcp_compare.py', 'runner.py', 'path_audit.py', 'COVERAGE_PLAN.md']: + path = Path(__file__).with_name(name) + shutil.copy2(path, args.output/name) + report['supportFiles'][name] = _sha256_file(path) + def save(): + (args.output/'run.json').write_text(json.dumps(report, indent=2)+'\n') + save() + for row in run['results']: + if row['question'] != 'hubs': + continue + if not row['executionSucceeded']: + raise ValueError('hub question failed; retain it as an incomplete input workflow') + name, tool = row['repository'], row['tool'] + repo = next(r for r in source['repositories'] if r['repository'] == name) + pinned = next(r for r in suite.repositories if r.name == name) + root, graph_path = Path(repo['source']), Path(repo[tool+'Graph']) + _verify_source(pinned, root) + digest = _sha256_file(graph_path) + if digest != repo[tool+'GraphSha256']: + raise ValueError('input graph changed') + graph = json.loads(read_bounded(graph_path, MAX_GRAPH_BYTES)) + checked = audit(row, graph) + choices = selectors(row) + if len(choices) != 10 or len(checked['hubs']) != 10: + raise ValueError('expected ten captured hubs; input workflow incomplete') + binary = run['servers'][tool]['executable'] + argv = [binary, 'serve'] if tool == 'compass' else [binary, '-m', 'graphify.serve'] + argv += ['--graph', str(graph_path), '--transport', 'stdio'] + failure = None + with StdioMcp(argv, root, args.output/'raw'/name/tool) as session: + try: + session.initialize() + except (OSError, RuntimeError, ValueError, TimeoutError) as error: + failure = str(error) + for (rank, selector, mode), oracle in zip(choices, checked['hubs']): + params = {'label': selector} + if tool == 'graphify': + params['token_budget'] = 262144 + result = {'repository': name, 'tool': tool, 'rank': rank, 'selectorMode': mode, + 'arguments': params, 'argv': argv, 'graphSha256': digest, + 'executionSucceeded': False, 'navigationResolved': False} + if failure is not None: + result['captureError'] = f'connection unavailable: {failure}' + else: + started = time.monotonic() + try: + packet = session.send('tools/call', {'name': 'get_neighbors', 'arguments': params}) + answer = packet.get('result', {}) + text = '\n'.join(c['text'] for c in answer.get('content', []) if c.get('type') == 'text') + result.update(response=packet, text=text, textBytes=len(text.encode()), + executionSucceeded='error' not in packet and not answer.get('isError', False)) + raw = session.directory/f'{packet["id"]:02}.response.jsonl' + result['wireResponseBytes'] = raw.stat().st_size + if result['executionSucceeded']: + result.update(check_navigation(text, graph, tool, oracle.get('id'))) + except (OSError, RuntimeError, ValueError, TimeoutError) as error: + failure = str(error) + result['captureError'] = failure + result['elapsedSeconds'] = time.monotonic() - started + report['results'].append(result) + save() + print(name, tool, rank, result['navigationResolved'], flush=True) + _verify_source(pinned, root) + if _sha256_file(graph_path) != digest: + raise ValueError('graph changed during navigation') + verify_environment(environment) + for record in run['servers'].values(): + if _sha256_file(Path(record['executable'])) != record['executableSha256']: + raise ValueError('server executable changed during navigation') + report['complete'] = True + save() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--run', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + main(parser.parse_args()) diff --git a/benchmarks/agent_query/mcp_audit.py b/benchmarks/agent_query/mcp_audit.py index cf22d7183..8e97b3302 100644 --- a/benchmarks/agent_query/mcp_audit.py +++ b/benchmarks/agent_query/mcp_audit.py @@ -53,15 +53,41 @@ def audit(row, graph): for a,b in {(e['source'],e['target']) for e in graph['links']}: degree[a]+=1;degree[b]+=1 hubs=[] - for rank,name,count in re.findall(r'^ (\d+)\. (.*) - (\d+) edges$',text,re.M): + displayed=re.findall(r'^ (\d+)\. (.*) - (\d+) edges$',text,re.M) + structured=row.get('response',{}).get('result',{}).get('structuredContent') + records=None + if structured is not None: + if structured.get('schema')!='compass.mcp.tool-result/1' or structured.get('result',{}).get('schema')!='compass.mcp.hubs/1': + raise ValueError('unsupported structured hub result') + records=structured['result']['nodes'] + if not isinstance(records,list) or len(records)!=len(displayed): + raise ValueError('structured and displayed hub counts disagree') + used_ids=set() + for position,(rank,name,count) in enumerate(displayed): matches=names.get(name,[]) entry={'rank':int(rank),'label':name,'degree':int(count),'identityCandidates':len(matches)} + if records is not None: + record=records[position] + identifier=record.get('id') + entry['explicitId']=identifier + valid=(isinstance(identifier,str) and identifier in nodes and identifier not in used_ids + and label(nodes[identifier],tool)==record.get('label') + and record.get('label')==name and record.get('degree')==int(count) + and type(record.get('degree')) is int + and type(record.get('rank')) is int and record.get('rank')==int(rank)==position+1) + matches=[nodes[identifier]] if valid else [] + entry['identityCandidates']=len(matches) + if valid:used_ids.add(identifier) if len(matches)==1: n=matches[0];file,line,_=_node_anchor(n,tool) entry.update(id=n['id'],file=file,line=line,expectedDegree=degree[n['id']],degreeMatches=degree[n['id']]==int(count)) + if records is not None: + entry['sourceAnchorMatches']=(record.get('sourceFile')==file and record.get('startLine')==line) hubs.append(entry) result.update(hubs=hubs,returned=len(hubs),verifiedIdentities=sum(x['identityCandidates']==1 for x in hubs), - matchingDegrees=sum(x.get('degreeMatches',False) for x in hubs)) + matchingDegrees=sum(x.get('degreeMatches',False) for x in hubs), + explicitIdentities=sum('explicitId' in x and x['identityCandidates']==1 for x in hubs), + matchingSourceAnchors=sum(x.get('sourceAnchorMatches',False) for x in hubs)) elif kind == 'neighbors': seed=row['arguments']['label'] actual=set(re.findall(r'^ (-->|<--) (.*?) \[([^\]]*)\] \[[^\]]*\]',text,re.M)) diff --git a/benchmarks/agent_query/tests/test_hub_navigation.py b/benchmarks/agent_query/tests/test_hub_navigation.py new file mode 100644 index 000000000..e4607c11c --- /dev/null +++ b/benchmarks/agent_query/tests/test_hub_navigation.py @@ -0,0 +1,31 @@ +import unittest + +from benchmarks.agent_query.hub_navigation import check_navigation, selectors + + +class HubNavigationTests(unittest.TestCase): + def test_legacy_selector_is_the_returned_label(self): + row={'response':{'result':{}},'text':'God nodes (most connected):\n 1. run() - 9 edges'} + self.assertEqual(selectors(row),[(1,'run()','display-label')]) + + def test_explicit_selector_preserves_id_bytes(self): + row={'response':{'result':{'structuredContent':{ + 'schema':'compass.mcp.tool-result/1', + 'result':{'schema':'compass.mcp.hubs/1','nodes':[{'id':'A"\\\n','rank':1}]}}}}} + self.assertEqual(selectors(row),[(1,'A"\\\n','explicit-id')]) + + def test_unknown_identity_cannot_be_resolved_by_matching_neighbors(self): + graph={'nodes':[{'id':'a','name':'Alpha'}],'links':[]} + result=check_navigation('Neighbors of Alpha:',graph,'compass',None) + self.assertFalse(result['navigationResolved']) + + def test_neighbor_labels_preserve_multiplicity_between_distinct_nodes(self): + graph={'nodes':[{'id':'a','name':'Alpha'},{'id':'b','name':'run()'}, + {'id':'c','name':'run()'}], + 'links':[{'source':'a','target':'b'},{'source':'a','target':'c'}, + {'source':'a','target':'b'}]} + one='Neighbors of Alpha:\n --> run() [calls] [EXTRACTED]' + self.assertFalse(check_navigation(one,graph,'compass','a')['navigationResolved']) + two=one+'\n --> run() [calls] [EXTRACTED]' + self.assertTrue(check_navigation(two,graph,'compass','a')['navigationResolved']) + self.assertFalse(check_navigation(two.replace('-->','<--'),graph,'compass','a')['navigationResolved']) diff --git a/benchmarks/agent_query/tests/test_mcp_audit.py b/benchmarks/agent_query/tests/test_mcp_audit.py index 46ecd0bb6..aca28d6d2 100644 --- a/benchmarks/agent_query/tests/test_mcp_audit.py +++ b/benchmarks/agent_query/tests/test_mcp_audit.py @@ -39,6 +39,29 @@ def test_degree_collapses_parallel_pairs_and_counts_self_loop_twice(self): r=audit(self.row('hubs','God nodes (most connected):\n 1. Alpha - 3 edges'),self.graph) self.assertEqual(r['matchingDegrees'],1) + def test_explicit_hub_identity_must_exist_and_match_source(self): + self.graph['nodes'][1]['name']='Alpha' + row=self.row('hubs','God nodes (most connected):\n 1. Alpha - 1 edges') + record={'id':'a','label':'Alpha','degree':1,'rank':1,'sourceFile':'a.rs','startLine':1} + row['response']={'result':{'structuredContent':{'schema':'compass.mcp.tool-result/1', + 'result':{'schema':'compass.mcp.hubs/1','nodes':[record]}}}} + checked=audit(row,self.graph) + self.assertEqual(checked['explicitIdentities'],1) + self.assertEqual(checked['matchingSourceAnchors'],1) + record['sourceFile']='b.rs' + self.assertEqual(audit(row,self.graph)['matchingSourceAnchors'],0) + record['id']='missing' + self.assertEqual(audit(row,self.graph)['verifiedIdentities'],0) + + def test_structured_hubs_cannot_hide_missing_or_duplicate_rows(self): + row=self.row('hubs','God nodes (most connected):\n 1. Alpha - 1 edges\n 2. Alpha - 1 edges') + nodes=[{'id':'a','label':'Alpha','degree':1,'rank':rank,'sourceFile':'a.rs','startLine':1} for rank in (1,2)] + row['response']={'result':{'structuredContent':{'schema':'compass.mcp.tool-result/1', + 'result':{'schema':'compass.mcp.hubs/1','nodes':nodes}}}} + self.assertEqual(audit(row,self.graph)['verifiedIdentities'],1) + nodes.pop() + with self.assertRaises(ValueError):audit(row,self.graph) + def test_silent_selection_is_not_ambiguity(self): r=audit(self.row('ambiguous-neighbors','Neighbors of ambiguous_name:\n --> Alpha [calls] [EXTRACTED]'),self.graph) self.assertFalse(r['ambiguityPreserved']) From e3652ae30424dd58da1d7e6d8bcdc17ec418a2c7 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:40:31 -0700 Subject: [PATCH 15/97] bench: preserve virtual environment in hub follow-ups --- benchmarks/agent_query/hub_navigation.py | 17 +++++++++++++++-- .../agent_query/tests/test_hub_navigation.py | 8 +++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/benchmarks/agent_query/hub_navigation.py b/benchmarks/agent_query/hub_navigation.py index 3452df37a..4cdfd6804 100644 --- a/benchmarks/agent_query/hub_navigation.py +++ b/benchmarks/agent_query/hub_navigation.py @@ -30,6 +30,18 @@ def selectors(row): re.findall(r'^ (\d+)\. (.*) - \d+ edges$', row['text'], re.M)] +def server_paths(run): + # A venv's Python symlink must not be replaced with its resolved interpreter: + # doing so discards that environment's site-packages. + paths = {} + for tool in ['compass', 'graphify']: + choices = {r['argv'][0] for r in run['results'] if r['tool'] == tool} + if len(choices) != 1: + raise ValueError('capture uses inconsistent server launch paths') + paths[tool] = next(iter(choices)) + return paths + + def check_navigation(text, graph, tool, seed): """Seed is oracle identity, never a substituted request selector.""" nodes = {n['id']: n for n in graph['nodes']} @@ -59,13 +71,14 @@ def main(args): # Verify the source capture, graphs, and complete raw RPC transcripts first. audit_capture(SimpleNamespace(run=args.run, output=args.output/'input-audit.json')) run = json.loads(read_bounded(args.run/'run.json')) + binaries = server_paths(run) source_run = Path(run['sourceRun']) source = json.loads(read_bounded(source_run/'run.json')) suite = load_suite(source_run/'suite.toml') if suite.digest != source['suiteDigest']: raise ValueError('source suite changed') environment = SimpleNamespace( - graphify_python=Path(run['servers']['graphify']['executable']), + graphify_python=Path(binaries['graphify']), graphify_environment=args.run/'graphify-environment.json') verify_environment(environment) for record in run['servers'].values(): @@ -101,7 +114,7 @@ def save(): choices = selectors(row) if len(choices) != 10 or len(checked['hubs']) != 10: raise ValueError('expected ten captured hubs; input workflow incomplete') - binary = run['servers'][tool]['executable'] + binary = binaries[tool] argv = [binary, 'serve'] if tool == 'compass' else [binary, '-m', 'graphify.serve'] argv += ['--graph', str(graph_path), '--transport', 'stdio'] failure = None diff --git a/benchmarks/agent_query/tests/test_hub_navigation.py b/benchmarks/agent_query/tests/test_hub_navigation.py index e4607c11c..916328e9f 100644 --- a/benchmarks/agent_query/tests/test_hub_navigation.py +++ b/benchmarks/agent_query/tests/test_hub_navigation.py @@ -1,9 +1,15 @@ import unittest -from benchmarks.agent_query.hub_navigation import check_navigation, selectors +from benchmarks.agent_query.hub_navigation import check_navigation, selectors, server_paths class HubNavigationTests(unittest.TestCase): + def test_server_launch_preserves_the_captured_virtual_environment(self): + run={'servers':{'graphify':{'executable':'/global/python'}}, + 'results':[{'tool':'graphify','argv':['/isolated/env/bin/python']}, + {'tool':'compass','argv':['/frozen/compass']}]} + self.assertEqual(server_paths(run)['graphify'],'/isolated/env/bin/python') + def test_legacy_selector_is_the_returned_label(self): row={'response':{'result':{}},'text':'God nodes (most connected):\n 1. run() - 9 edges'} self.assertEqual(selectors(row),[(1,'run()','display-label')]) From 6c84f44c10d36af7f3962492afaea04ab662985a Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:45:21 -0700 Subject: [PATCH 16/97] fix: preserve MCP hub identity for direct navigation --- CHANGELOG.md | 4 + COMPATIBILITY.md | 15 ++ crates/compass-mcp/src/lib.rs | 138 ++++++++++++++++-- crates/compass-mcp/tests/coverage_paths.rs | 7 + crates/compass-query/src/score.rs | 24 ++- ...ode-graph-intelligence-audit-2026-09-26.md | 101 ++++++++++++- docs/reference/outputs.md | 39 +++++ 7 files changed, 312 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f44292ad5..57953193e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Include exact node IDs and source locations in MCP hub results, with a + versioned structured projection for follow-up navigation. Describe hubs as + topology candidates rather than established design defects. + - Preserve community IDs alongside labels in traversal caches, restoring MCP community membership and statistics on typed graphs. Older disposable traversal caches rebuild automatically. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index d47b13537..3e8a5c6e2 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -94,6 +94,21 @@ history profiles, and cache identities. ## Evolving contracts +### MCP hub identities + +MCP `god_nodes` adds `structuredContent` using the existing +`compass.mcp.tool-result/1` envelope and the result schema +`compass.mcp.hubs/1`. The ranked records preserve exact IDs, kinds, degrees, +and available source anchors. Text and the `compass://god-nodes` resource add +an ID/source/location line beneath each entry; string values are JSON-escaped +so IDs remain recoverable without injecting extra lines. Missing anchors are +null. Inputs, eligibility, degree calculation, and ranking are unchanged. +Exact node lookup checks the original ID before the existing whitespace-trimmed +fallback, preserving distinct legacy IDs that differ by surrounding whitespace. +The non-transport `CompassMcp::invoke` compatibility helper still returns text +for this tool. Machine consumers should use the structured projection rather +than parse display labels. See [output contracts](docs/reference/outputs.md#mcp-hub-results). + ### MCP community and neighbor lookup The disposable traversal cache now retains a labeled community's numeric ID diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 16d13fc99..f45a17d58 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -369,6 +369,10 @@ impl CompassMcp { pub fn invoke(&self, name: &str, mut arguments: Map) -> String { self.invoke_result(name, &mut arguments) .map(|result| { + // Keep this legacy text helper stable; MCP carries both projections. + if name == "god_nodes" { + return result.text; + } result .structured_content .map_or(result.text, |value| value.to_string()) @@ -581,6 +585,9 @@ impl CompassMcp { if typed_query { return invoke_typed_tool(&self.store, name, arguments, &context.path, Some(&context)); } + if name == "god_nodes" { + return invoke_hub_tool(arguments, &context); + } Ok(ToolInvocation { text: invoke_tool(name, arguments, &context).map_err(InvocationError::InvalidParams)?, structured_content: None, @@ -1360,7 +1367,7 @@ fn tool_specs() -> Vec { ), tool( "god_nodes", - "Return the most connected nodes - the core abstractions of the knowledge graph.", + "Return connected hub candidates with exact IDs, source anchors, and degree. Connectivity is a topology observation, not proof of excessive responsibility.", json!({"type":"object","properties":{"top_n":{"type":"integer","default":10}}}), ), tool( @@ -2331,16 +2338,59 @@ fn tool_god_nodes( arguments: &Map, context: &GraphContext, ) -> Result { + invoke_hub_tool(arguments, context) + .map(|result| result.text) + .map_err(|error| error.to_string()) +} + +fn invoke_hub_tool( + arguments: &Map, + context: &GraphContext, +) -> Result { let top_n = usize::try_from(integer_argument(arguments, "top_n", 10).max(0)).unwrap_or_default(); - let nodes = god_nodes(&context.document()?, top_n); + let document = context.document()?; + let nodes = god_nodes(&document, top_n); + let records = document + .nodes + .iter() + .map(|node| (node.id.as_str(), node)) + .collect::>(); let mut lines = vec!["God nodes (most connected):".to_owned()]; - lines.extend( - nodes.iter().enumerate().map(|(index, node)| { - format!(" {}. {} - {} edges", index + 1, node.label, node.degree) - }), - ); - Ok(lines.join("\n")) + let mut identities = Vec::with_capacity(nodes.len()); + for (index, node) in nodes.iter().enumerate() { + let record = records.get(node.id.as_str()).ok_or_else(|| { + InvocationError::Internal("hub identity is absent from the selected graph".to_owned()) + })?; + let location = record.string("source_location"); + let source_location = (!location.is_empty()).then_some(location.as_str()); + lines.push(format!( + " {}. {} - {} edges", + index + 1, + sanitize_label(&node.label), + node.degree + )); + lines.push(format!( + " id: {} | source: {} | location: {}", + json!(node.id), + json!(record.source_file()), + json!(source_location) + )); + identities.push(json!({ + "rank":index + 1, "id":node.id, "label":node.label, "degree":node.degree, + "kind":record.kind_name(), "sourceFile":record.source_file(), + "sourceLocation":source_location, "startLine":record.unsigned("line_start"), + "endLine":record.unsigned("line_end") + })); + } + let structured = transport_envelope(json!({ + "schema":"compass.mcp.hubs/1", "ranking":"distinct-directed-endpoint-degree", + "interpretation":"topology-candidates", "requested":top_n, "nodes":identities + }))?; + Ok(ToolInvocation { + text: lines.join("\n"), + structured_content: Some(structured), + }) } fn tool_graph_stats(context: &GraphContext) -> String { @@ -2884,6 +2934,70 @@ fn read_bounded_resource(path: &Path) -> Result { mod tests { use super::*; + #[test] + fn hub_results_preserve_exact_identity_and_source_anchors() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("graph.json"); + let unusual_id = "A\"\\\n"; + fs::write( + &path, + serde_json::to_vec(&json!({ + "directed":true, + "nodes":[ + {"id":unusual_id,"name":"run()","kind":"function", + "source":{"file":"src/a.rs","startLine":7,"endLine":9}}, + {"id":"b","name":"run()","kind":"function", + "source":{"file":"src/b.rs","startLine":12}}, + {"id":"legacy","label":"Legacy","source_file":"legacy.rs", + "source_location":"L20"} + ], + "links":[{"source":unusual_id,"target":"b","kind":"calls"}, + {"source":"legacy","target":"b","relation":"calls"}] + }))?, + )?; + let server = CompassMcp::new(&path); + let output = server + .invoke_result("god_nodes", &mut Map::new()) + .map_err(|error| error.to_string())?; + let content = output + .structured_content + .ok_or("missing structured hub identities")?; + assert_eq!(content["schema"], MCP_TOOL_RESULT_SCHEMA); + assert_eq!(content["transportTruncation"]["truncated"], false); + assert_eq!(content["result"]["schema"], "compass.mcp.hubs/1"); + let nodes = content["result"]["nodes"] + .as_array() + .ok_or("missing hub nodes")?; + assert_eq!(nodes.len(), 3); + assert_eq!(nodes[0]["id"], "b"); + assert_eq!(nodes[0]["degree"], 2); + let unusual = nodes + .iter() + .find(|node| node["id"] == unusual_id) + .ok_or("lost exact ID")?; + assert_eq!(unusual["sourceFile"], "src/a.rs"); + assert_eq!(unusual["startLine"], 7); + assert_eq!(unusual["endLine"], 9); + let legacy = nodes + .iter() + .find(|node| node["id"] == "legacy") + .ok_or("lost legacy hub")?; + assert_eq!(legacy["sourceLocation"], "L20"); + assert_eq!(legacy["startLine"], Value::Null); + assert!(output.text.contains(&format!("id: {}", json!(unusual_id)))); + assert!(!output.text.contains(unusual_id)); + for node in nodes { + let id = node["id"].as_str().ok_or("ID not a string")?; + let neighbors = server.invoke( + "get_neighbors", + Map::from_iter([("label".into(), json!(id))]), + ); + assert!(neighbors.starts_with("Neighbors of "), "{neighbors}"); + } + Ok(()) + } + #[test] fn mcp_neighbor_filter_precedes_neighbor_grouping() -> Result<(), Box> { let temp = tempfile::tempdir()?; @@ -3326,12 +3440,12 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. Counter - 1 edges\n 2. Path - 1 edges" + "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null\n 2. Path - 1 edges\n id: \"z\" | source: \"src/path.rs\" | location: null" ); let arguments = Map::from_iter([("top_n".to_owned(), json!(1))]); assert_eq!( server.invoke("god_nodes", arguments), - "God nodes (most connected):\n 1. Counter - 1 edges" + "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null" ); Ok(()) } @@ -3357,7 +3471,7 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. .dispatch() - 1 edges" + "God nodes (most connected):\n 1. .dispatch() - 1 edges\n id: \"method\" | source: \"src/service.rs\" | location: \"L5\"" ); Ok(()) } @@ -3383,7 +3497,7 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. helper() - 1 edges\n 2. prepare() - 1 edges" + "God nodes (most connected):\n 1. helper() - 1 edges\n id: \"helper\" | source: \"src/support.sh\" | location: \"L1\"\n 2. prepare() - 1 edges\n id: \"prepare\" | source: \"bin/launch\" | location: \"L2\"" ); Ok(()) } diff --git a/crates/compass-mcp/tests/coverage_paths.rs b/crates/compass-mcp/tests/coverage_paths.rs index 2428049c1..1f4fd4e1c 100644 --- a/crates/compass-mcp/tests/coverage_paths.rs +++ b/crates/compass-mcp/tests/coverage_paths.rs @@ -306,6 +306,13 @@ async fn in_memory_protocol_exercises_tool_and_resource_server_handlers() .call_tool(CallToolRequestParams::new("graph_stats")) .await?; assert!(!call.content.is_empty()); + let hubs = client + .call_tool(CallToolRequestParams::new("god_nodes")) + .await?; + assert!(!hubs.content.is_empty()); + let structured = hubs.structured_content.ok_or("missing structured hubs")?; + assert_eq!(structured["result"]["schema"], "compass.mcp.hubs/1"); + assert!(structured["result"]["nodes"].as_array().is_some()); assert!( client .read_resource(ReadResourceRequestParams::new("compass://report")) diff --git a/crates/compass-query/src/score.rs b/crates/compass-query/src/score.rs index 237957009..50e2cbeec 100644 --- a/crates/compass-query/src/score.rs +++ b/crates/compass-query/src/score.rs @@ -424,7 +424,10 @@ pub fn pick_seeds( #[must_use] pub fn find_node(graph: &Graph, label: &str) -> Vec { - if let Some(index) = graph.node_index(label.trim()) { + if let Some(index) = graph + .node_index(label) + .or_else(|| graph.node_index(label.trim())) + { return vec![index]; } let term = search_tokens(label).join(" "); @@ -704,6 +707,25 @@ mod tests { score_nodes, score_nodes_with_profile, singleton_score, }; + #[test] + fn exact_node_id_precedes_whitespace_normalization() -> Result<(), Box> { + let graph = Graph::from_document(serde_json::from_value(json!({ + "directed":true, + "nodes":[{"id":" A\n","label":"run"},{"id":"A","label":"run"}], + "links":[] + }))?)?; + for query in [" A\n", "A"] { + let matches = super::find_node(&graph, query); + assert_eq!(matches.len(), 1); + assert_eq!(graph.node(matches[0]).id, query); + } + let padded = super::find_node(&graph, " A "); + assert_eq!(padded.len(), 1); + assert_eq!(graph.node(padded[0]).id, "A"); + assert_eq!(super::find_node(&graph, "run").len(), 2); + Ok(()) + } + fn seed(score: f64, degree: usize, label_len: usize, id: &str) -> BestSeed { BestSeed { score, diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 638476248..d2bc55053 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -625,14 +625,109 @@ extraction or performance claim follows. Replay `mcp-paired-03` completes all `mcp-paired-02` for both tools. The separate Flask diagnostic is the evidence for the additional filter correction. +### Hub identity and direct navigation + +The first hub responses omitted the exact IDs already retained by Compass's +analysis layer. MCP now adds ID/source/location text and the versioned +`compass.mcp.hubs/1` structured result inside its existing transport envelope. +It preserves ranking and degree semantics. Labels are sanitized for display; +exact IDs are JSON-escaped in text and retained unchanged in structured data. +Missing source fields remain null. The public description now describes +topology candidates rather than asserting that connectivity proves a core +abstraction or design defect. + +A failed-before native regression covers duplicate labels, legacy source +locations, typed anchors, and an ID containing quotes, a backslash, and a +newline. Its follow-up also exposed lookup trimming an ID before checking exact +identity. Exact lookup now precedes the existing trimmed fallback; a separate +query-layer test verifies IDs distinguished by surrounding whitespace. +The transport regression checks that clients receive structured hub results. + +`mcp-paired-04` repeats all 58 original RPCs. All previous graph-consistency +checks remain passing for both tools. All 50 Compass hub IDs now resolve to +unique graph nodes, with independently matching degrees and source anchors. +Graphify's MCP result still provides label-only identity: 37/50 entries can be +identified uniquely from their display labels. Its CLI JSON interface supplies +IDs, so this is specifically a finding about these MCP responses. Neither +count proves complete top-N eligibility, source precision, or design quality. + +Commit `7c554c2f` adds a separate development diagnostic before its follow-up +requests: use each returned hub ID when available, otherwise the returned +label, for exactly one `get_neighbors` request. The graph may be read by the +oracle but never to substitute a request ID. Explicit ambiguity is safe and +is not counted as completed direct navigation. The same full-enumeration +bounds apply to both tools. Additional disambiguation steps and the alternative +Graphify CLI workflow are outside this diagnostic. + +| Repository | Compass before IDs | Compass with IDs | Graphify in both runs | +| --- | ---: | ---: | ---: | +| Cobra | 2/10 | 10/10 | 7/10 | +| Flask | 2/10 | 10/10 | 5/10 | +| Gson | 0/10 | 10/10 | 7/10 | +| Zod | 0/10 | 10/10 | 4/10 | +| Axum | 0/10 | 10/10 | 5/10 | +| Total | 4/50 | 50/50 | 28/50 | + +Runs `hub-navigation-02` and `hub-navigation-03` each capture all 100 follow-up +RPCs without execution failures. The oracle checks seed identity, response +headline, and the multiset of displayed direction/neighbor-label pairs after +grouping by distinct neighbor. It does not validate individual neighbor IDs, +all parallel relations, source occurrences, or source accuracy. Both products' +broader name matching can be ambiguous even when a case-sensitive display +label is unique. The initial `hub-navigation-01` attempt stopped before +follow-up RPCs because the collector resolved Python's virtual-environment +symlink, losing its package environment. Commit `e3652ae3` fixes that harness +error; it is not counted as a product failure. + +The five hub responses plus 50 follow-ups total 593,962 text bytes / 626,338 +wire-response bytes for corrected Compass, versus 265,393 / 272,996 for +Graphify. These totals include different completion counts and different +returned hub sets, and are not an efficiency win. Initialization/setup +transcripts are retained separately. A separate transcript integrity pass +checks all 200 follow-up requests and responses against their recorded +arguments, text, and byte counts. Timings remain unsuitable for speed claims. + +The frozen executable in `mcp-hubs-provenance` has SHA256 +`0c54260eb380c08c18a14c7044a1abce932ede56114d6a515223ab19e1b7be93`. +Verification passes 1,198 native tests, zero failed, two ignored, including +the broader `coverage_paths` suites and the new identity regressions. +Workspace and `coverage_paths` Clippy pass with warnings denied. All 72 Python +benchmark tests, formatting, diff checks, and product boundary pass. Full +extraction fixture qualification remains the receiver checkpoint; this change +adds query/MCP presentation and exact-ID lookup verification on retained graphs. + +### Source-role diagnostics from the returned hubs + +Two post-output reductions in `hub-source-diagnostics-01.json` retain pinned +source excerpts, file and graph hashes, node records, and selected edges: + +- Compass's Zod `to-json-schema.test` module spans the test suite, not a + production object. Its distinct-pair degree is 789. Its 1,156 incident edge + records include 789 containment, 321 reference, and 46 call records, with + overlap between endpoint pairs. Containment-driven degree does not establish + excessive responsibility. +- Graphify's Axum hubs `S` at `src/service_ext.rs:47` and `T` at + `src/handler/mod.rs:272` are generic implementation subjects. Their degrees + are 153 and 87, predominantly reference edges. Two reviewed edges target + the ServiceExt implementation's `S` from `Router` and the separate + `Handler<..., S> for T` implementation. Each source declaration binds its + own `S`; those references do not identify the independently bound `S` in + `service_ext.rs`. This establishes two wrong reference targets, not that all + 150 references to that hub are wrong or a representative precision rate. + +These diagnostics motivate role-aware explanations and independent edge +review. They are not folded into the original graph-consistency scores, and +do not establish that Compass already diagnoses god objects reliably. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. -2. Add unambiguous hub identities and source anchors, then expand hub review - to source-reviewed design judgments. Evaluate cluster responsibilities and - cross-community connections separately from graph consistency. +2. Expand the now-identifiable hubs into source-reviewed role and design + judgments, including containment-dominated modules and generic reference + targets. Evaluate cluster responsibilities and cross-community connections + separately from graph consistency. 3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, traversal direction, source occurrences, ambiguity, unreachable nodes, and bound exhaustion. A negative or limit outcome must never count as a path. diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 7cbf2db3b..5fd507941 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -822,6 +822,45 @@ When exact automation is required, use: - diff JSON; - direct graph JSON. +### MCP hub results + +MCP `god_nodes` returns human-readable text and a structured projection in +`structuredContent.result`, inside `compass.mcp.tool-result/1`: + +```json +{ + "schema": "compass.mcp.hubs/1", + "ranking": "distinct-directed-endpoint-degree", + "interpretation": "topology-candidates", + "requested": 10, + "nodes": [{ + "rank": 1, + "id": "exact-node-id", + "label": "dispatch()", + "degree": 12, + "kind": "method", + "sourceFile": "src/service.rs", + "sourceLocation": "L5", + "startLine": 5, + "endLine": null + }] +} +``` + +Ranks are one-based. Degree counts distinct stored directed endpoint pairs; +parallel occurrences collapse and a self-loop contributes two. Eligible hubs +are ordered by descending degree, then exact ID. Eligibility remains the +existing hub-analysis policy; file/concept/JSON-key nodes and isolates are +excluded. This is a topology ranking, not a diagnosis of excessive +responsibility. Review source responsibilities and relationship evidence +before making a design judgment. + +Use `id` as the next `get_neighbors.label` input. Source fields are null when +not present in the graph. A legacy textual location can exist without numeric +line fields; the result preserves it without inventing a line number. The +existing 16 MiB structured-response bound applies and fails explicitly rather +than silently dropping entries. Consumers must check the schema version. + ### Agent Query View The focused query commands and MCP query tools also expose the strict, From 11d731e2b538da2b432652617d972a799b13ed77 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:54:07 -0700 Subject: [PATCH 17/97] bench: add multilingual MCP path and failure diagnostics --- benchmarks/agent_query/mcp_compare.py | 20 + benchmarks/agent_query/mcp_path_audit.py | 151 +++ benchmarks/agent_query/suite_mcp_paths.json | 1013 +++++++++++++++++ .../agent_query/tests/test_mcp_paths.py | 69 ++ 4 files changed, 1253 insertions(+) create mode 100644 benchmarks/agent_query/mcp_path_audit.py create mode 100644 benchmarks/agent_query/suite_mcp_paths.json create mode 100644 benchmarks/agent_query/tests/test_mcp_paths.py diff --git a/benchmarks/agent_query/mcp_compare.py b/benchmarks/agent_query/mcp_compare.py index cc4f19950..ab4cf7929 100644 --- a/benchmarks/agent_query/mcp_compare.py +++ b/benchmarks/agent_query/mcp_compare.py @@ -23,6 +23,20 @@ def community(node, tool): def prepare_questions(graph, tool, witness): + if 'pathQuestions' in witness: + queries = [] + for question in witness['pathQuestions']: + arguments = question['arguments'][tool] + if set(arguments) - {'source', 'target', 'max_hops', 'undirected'}: + raise ValueError('unexpected prepared path argument') + if not all(isinstance(arguments.get(key), str) and arguments[key] for key in ['source', 'target']): + raise ValueError('invalid prepared path endpoints') + if type(arguments.get('max_hops')) is not int or not 0 <= arguments['max_hops'] <= 64: + raise ValueError('invalid prepared hop bound') + if tool == 'graphify' and arguments.get('undirected') is not True: + raise ValueError('shared navigation tasks require explicit undirected Graphify search') + queries.append((question['id'], 'shortest_path', arguments)) + return queries matches = [n for n in graph['nodes'] if _node_anchor(n, tool)[:2] == (witness['file'], witness['line']) and witness['symbol'] in _node_anchor(n, tool)[2]] if len(matches) != 1: @@ -94,11 +108,17 @@ def execute(args): source = Path(repo['source']) _verify_source(pinned,source) check_source(source, {'file':witness['file'],'line':witness['line'],'text':witness['sourceText']}) + if witness.get('commit', repo['commit']) != repo['commit']: + raise ValueError('prepared source commit differs') + for anchor in witness.get('anchors', []): + check_source(source, anchor) for tool in ['compass','graphify']: graph_path = Path(repo[tool+'Graph']) digest = _sha256_file(graph_path) if digest != repo[tool+'GraphSha256']: raise ValueError('captured graph changed') + if witness.get('graphSha256', {}).get(tool, digest) != digest: + raise ValueError('prepared path graph differs') graph = json.loads(read_bounded(graph_path,MAX_GRAPH_BYTES)) questions = prepare_questions(graph,tool,witness) argv = [str(args.compass),'serve'] if tool == 'compass' else [str(args.graphify_python),'-m','graphify.serve'] diff --git a/benchmarks/agent_query/mcp_path_audit.py b/benchmarks/agent_query/mcp_path_audit.py new file mode 100644 index 000000000..1dec2d169 --- /dev/null +++ b/benchmarks/agent_query/mcp_path_audit.py @@ -0,0 +1,151 @@ +"""Audit MCP navigation against stored topology and separate reviewed source paths.""" +import argparse +from collections import deque +import json +from pathlib import Path +import re +from types import SimpleNamespace + +from benchmarks.agent_query.mcp_audit import label, main as verify_capture +from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES +from benchmarks.agent_query.runner import _node_anchor, _sha256_file + +REL = r'[a-z_]+(?:/[a-z_]+)*' +CONF = r'(?: \[[A-Z_]+(?:/[A-Z_]+)*\])?' +EDGE = re.compile(r'--('+REL+')'+CONF+r'-->|<--('+REL+')'+CONF+r'--') + + +def shortest_distance(graph, source, target): + adjacency = {n['id']: set() for n in graph['nodes']} + for e in graph['links']: + adjacency[e['source']].add(e['target']) + adjacency[e['target']].add(e['source']) + if source not in adjacency or target not in adjacency: + return None + queue = deque([(source, 0)]) + seen = {source} + while queue: + node, distance = queue.popleft() + if node == target: + return distance + for other in adjacency[node]: + if other not in seen: + seen.add(other) + queue.append((other, distance+1)) + return None + + +def parse_path(text): + headers = list(re.finditer(r'^Shortest path \((\d+) hops\):\n (.+)$', text, re.M)) + if len(headers) != 1: + raise ValueError('expected one actual path body') + h = headers[0] + if text[h.end():].strip(): + raise ValueError('unexpected trailing answer') + hops = int(h[1]) + if hops > 64: + raise ValueError('hop count outside audit bound') + body = h[2]; labels = []; steps = []; offset = 0 + for match in EDGE.finditer(body): + labels.append(body[offset:match.start()].strip()) + steps.append({'relations': (match[1] or match[2]).split('/'), + 'direction': 'forward' if match[1] else 'reverse'}) + offset = match.end() + labels.append(body[offset:].strip()) + if hops != len(steps) or any(not x for x in labels): + raise ValueError('path body differs from printed hop count') + return labels, steps + + +def audit(row, graph, question): + tool = row['tool']; args = row['arguments']; expected = question['expected']['outcome'] + text = row.get('text', '') or row.get('response', {}).get('error', {}).get('message', '') + result = {'repository': row['repository'], 'tool': tool, 'question': row['question'], + 'expected': expected, 'matched': False, 'executionSucceeded': row['executionSucceeded']} + if 'captureError' in row: + result['failure'] = row['captureError']; return result + positive = 'Shortest path (' in text + if expected == 'unresolved': + result['matched'] = not positive and 'No node matching source' in text and args['source'] in text + return result + if expected == 'ambiguous': + result['matched'] = not positive and 'ambig' in text.lower() + result['selectedDespiteAmbiguity'] = positive + return result + distance = shortest_distance(graph, args['source'], args['target']) + result['storedShortestHops'] = distance + if expected == 'depth-limit': + result['matched'] = distance is not None and distance > args['max_hops'] and not positive and 'max_hops' in text + return result + if expected == 'disconnected': + nodes = {n['id']: n for n in graph['nodes']} + expected_text = f"No path found between '{label(nodes[args['source']],tool)}' and '{label(nodes[args['target']],tool)}'." + result['matched'] = distance is None and text == expected_text + return result + try: + labels, steps = parse_path(text) + except ValueError as error: + result['failure'] = str(error); return result + names = {} + for node in graph['nodes']: + names.setdefault(label(node, tool), []).append(node) + if any(len(names.get(name, [])) != 1 for name in labels): + result['failure'] = 'path contains an unverified display identity'; return result + nodes = [names[name][0] for name in labels] + ids = [n['id'] for n in nodes] + result['nodeIds'] = ids + failures = [] + if ids[0] != args['source'] or ids[-1] != args['target']: + failures.append('wrong endpoint identity') + if len(steps) != distance or len(steps) > args['max_hops']: + failures.append('path is not minimum-hop within the requested bound') + for left, right, step in zip(ids, ids[1:], steps): + source, target = (left, right) if step['direction'] == 'forward' else (right, left) + relations = set() + for edge in graph['links']: + a, b = edge['source'], edge['target'] + if tool == 'graphify': + a, b = edge.get('_src', a), edge.get('_tgt', b) + if (a, b) == (source, target): + relations.add(edge.get('kind' if tool == 'compass' else 'relation', 'related')) + if not set(step['relations']) <= relations: + failures.append('printed relation/direction lacks a stored witness') + result.update(matched=not failures, failures=failures, steps=steps) + witness = question['expected'].get('sourceWitness') + if witness: + result['matchesReviewedSourceRoute'] = ( + not failures and len(nodes) == len(witness['nodes']) + and all(_node_anchor(n, tool)[:2] == (a['file'], a['line']) for n,a in zip(nodes,witness['nodes'])) + and all(s['direction'] == w['direction'] and set(s['relations']) <= set(w['relations']) + for s,w in zip(steps,witness['steps']))) + return result + + +def main(args): + verify_capture(SimpleNamespace(run=args.run, output=args.output.with_suffix('.capture.json'))) + run = json.loads(read_bounded(args.run/'run.json')) + inputs = json.loads(read_bounded(args.run/'inputs.json')) + source = json.loads(read_bounded(Path(run['sourceRun'])/'run.json')) + graphs = {}; results = [] + for row in run['results']: + key = row['repository'], row['tool'] + if key not in graphs: + repo = next(r for r in source['repositories'] if r['repository'] == key[0]) + graphs[key] = json.loads(read_bounded(Path(repo[key[1]+'Graph']), MAX_GRAPH_BYTES)) + spec = next(r for r in inputs['repositories'] if r['name'] == key[0]) + question = next(q for q in spec['pathQuestions'] if q['id'] == row['question']) + if row['arguments'] != question['arguments'][key[1]]: + raise ValueError('request arguments differ from preregistration') + checked = audit(row, graphs[key], question); results.append(checked) + print(key, row['question'], checked['matched'], checked.get('failure', checked.get('failures', []))) + report = {'scope': __doc__, 'runSha256': _sha256_file(args.run/'run.json'), + 'auditorSha256': _sha256_file(Path(__file__)), 'results': results} + with args.output.open('x') as stream: + json.dump(report, stream, indent=2) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--run', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + main(parser.parse_args()) diff --git a/benchmarks/agent_query/suite_mcp_paths.json b/benchmarks/agent_query/suite_mcp_paths.json new file mode 100644 index 000000000..c42a9d9c8 --- /dev/null +++ b/benchmarks/agent_query/suite_mcp_paths.json @@ -0,0 +1,1013 @@ +{ + "schema": "compass.mcp-comparison-inputs/1", + "scope": "Development MCP shortest-path comparison on pinned real graphs. Exact IDs are prepared symmetrically from source anchors; this is not measured node retrieval. All tasks use undirected navigation. Disconnected pairs are selected from both graphs and measure graph consistency, not source execution reachability. Previously reviewed positive witnesses remain post-output development witnesses; inputs were written before the first new RPCs but the intended pre-execution commit was prevented by a benchmark-test failure; the first capture is a development diagnostic, not a preregistered run.", + "policy": { + "positive": "Verify actual ordered node chain, stored edge relation and displayed direction, and minimum hop distance. Ambiguous display labels leave identity unverified. Separately compare the reviewed source witness.", + "hop-limit": "No positive path beyond max_hops; an explicit bound outcome is not global disconnection.", + "missing": "Unknown endpoint must remain unresolved, not match a convenient fuzzy node.", + "ambiguous": "At least two exact terminal-label declarations match. Refuse to choose; a warning followed by a path is not preserved ambiguity.", + "disconnected": "A symmetrically selected source-anchored pair lies in different undirected components in each graph. Require no path; no graph modification or synthetic fixture.", + "bounds": "Same 60-second/16-MiB-RPC and 64-MiB-session limits. max_hops=8 for positives, ambiguity, missing, and disconnected; one fewer than the shorter stored distance for the bound case. Graphify gets undirected=true; Compass currently exposes undirected navigation only." + }, + "repositories": [ + { + "name": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "file": "command.go", + "line": 757, + "sourceText": "func (c *Command) Find(", + "graphSha256": { + "compass": "6f862024bd68b88b734a43421a21cc2074a94ab7d68cc8857d59609112c9c7db", + "graphify": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + "anchors": [ + { + "file": "command.go", + "line": 757, + "text": "func (c *Command) Find(", + "labels": [ + ".Find()" + ] + }, + { + "file": "args.go", + "line": 28, + "text": "func legacyArgs(", + "labels": [ + "legacyArgs()" + ] + } + ], + "pathQuestions": [ + { + "id": "path-forward", + "arguments": { + "compass": { + "source": "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8", + "target": "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5", + "max_hops": 8 + }, + "graphify": { + "source": "cobra_command_find", + "target": "args_legacyargs", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "question": "cobra2-path-find-legacyargs", + "category": "call", + "nodes": [ + { + "file": "command.go", + "line": 757, + "text": "func (c *Command) Find(", + "labels": [ + ".Find()" + ] + }, + { + "file": "args.go", + "line": 28, + "text": "func legacyArgs(", + "labels": [ + "legacyArgs()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "command.go", + "line": 776, + "text": "legacyArgs(commandFound, stripFlags(a, commandFound))" + } + } + ] + } + } + }, + { + "id": "path-reverse", + "arguments": { + "compass": { + "source": "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5", + "target": "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8", + "max_hops": 8 + }, + "graphify": { + "source": "args_legacyargs", + "target": "cobra_command_find", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-hop-limit", + "arguments": { + "compass": { + "source": "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8", + "target": "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5", + "max_hops": 0 + }, + "graphify": { + "source": "cobra_command_find", + "target": "args_legacyargs", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-missing-source", + "arguments": { + "compass": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5", + "max_hops": 8 + }, + "graphify": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "args_legacyargs", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "unresolved" + } + } + ] + }, + { + "name": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "file": "src/flask/app.py", + "line": 995, + "sourceText": "def full_dispatch_request(", + "graphSha256": { + "compass": "227fe2d1f74cf835caa3ec15897f05544832fbf7f394e1c02e5134101a07c3ad", + "graphify": "ca35435af286c5c565fc66f55de569c2e3235f75c902182290ee15603ff103cc" + }, + "anchors": [ + { + "file": "src/flask/app.py", + "line": 995, + "text": "def full_dispatch_request(", + "labels": [ + ".full_dispatch_request()" + ] + }, + { + "file": "src/flask/app.py", + "line": 1024, + "text": "def finalize_request(", + "labels": [ + ".finalize_request()" + ] + }, + { + "file": "docs/conf.py", + "line": 1, + "text": "import packaging.version" + }, + { + "file": "examples/celery/make_celery.py", + "line": 1, + "text": "from task_app import create_app" + } + ], + "pathQuestions": [ + { + "id": "path-forward", + "arguments": { + "compass": { + "source": "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5", + "target": "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "max_hops": 8 + }, + "graphify": { + "source": "src_flask_app_flask_full_dispatch_request", + "target": "src_flask_app_flask_finalize_request", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "question": "flask2-path-fulldispatch-finalize", + "category": "call", + "nodes": [ + { + "file": "src/flask/app.py", + "line": 995, + "text": "def full_dispatch_request(", + "labels": [ + ".full_dispatch_request()" + ] + }, + { + "file": "src/flask/app.py", + "line": 1024, + "text": "def finalize_request(", + "labels": [ + ".finalize_request()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/flask/app.py", + "line": 1022, + "text": "self.finalize_request(ctx, rv)" + } + } + ] + } + } + }, + { + "id": "path-reverse", + "arguments": { + "compass": { + "source": "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "target": "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5", + "max_hops": 8 + }, + "graphify": { + "source": "src_flask_app_flask_finalize_request", + "target": "src_flask_app_flask_full_dispatch_request", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-hop-limit", + "arguments": { + "compass": { + "source": "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5", + "target": "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "max_hops": 0 + }, + "graphify": { + "source": "src_flask_app_flask_full_dispatch_request", + "target": "src_flask_app_flask_finalize_request", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-missing-source", + "arguments": { + "compass": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "max_hops": 8 + }, + "graphify": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "src_flask_app_flask_finalize_request", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "unresolved" + } + }, + { + "id": "path-ambiguous-source", + "arguments": { + "compass": { + "source": "url_for", + "target": "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "max_hops": 8 + }, + "graphify": { + "source": "url_for", + "target": "src_flask_app_flask_finalize_request", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "ambiguous" + } + }, + { + "id": "path-disconnected", + "arguments": { + "compass": { + "source": "sha256:1a76cfec23de97677c659fd8d7c5754a2e5fad9a6451a6acb4924916e8b310c4", + "target": "sha256:d7fad8c6dfcfa1e4a1f83ff180b467116358f7d99f058814c19fefd1cb6dc95f", + "max_hops": 8 + }, + "graphify": { + "source": "docs_conf", + "target": "examples_celery_make_celery", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "disconnected" + } + } + ] + }, + { + "name": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "sourceText": "public JsonWriter newJsonWriter(", + "graphSha256": { + "compass": "5856ddc99ec4025c9e36d128783e6e7393f45af76f617caa38fd6938aca23ea7", + "graphify": "92471ab8f5bb5d34aedddd2263fa24a43ae4ed31b34d06c8d433fe0f0dada447" + }, + "anchors": [ + { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(", + "labels": [ + ".newJsonWriter()" + ] + }, + { + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "text": "public class JsonWriter", + "labels": [ + "JsonWriter" + ] + }, + { + "file": "extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java", + "line": 1, + "text": "/*" + }, + { + "file": "gson/src/main/java/com/google/gson/annotations/package-info.java", + "line": 1, + "text": "/*" + } + ], + "pathQuestions": [ + { + "id": "path-forward", + "arguments": { + "compass": { + "source": "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1", + "target": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "max_hops": 8 + }, + "graphify": { + "source": "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter", + "target": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "question": "gson2-path-newjsonwriter-jsonwriter", + "category": "construction-reference", + "nodes": [ + { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(", + "labels": [ + ".newJsonWriter()" + ] + }, + { + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "text": "public class JsonWriter", + "labels": [ + "JsonWriter" + ] + } + ], + "steps": [ + { + "relations": [ + "instantiates", + "references" + ], + "direction": "forward", + "sitesByRelation": { + "instantiates": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 801, + "text": "new JsonWriter(writer)" + }, + "references": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(" + } + } + } + ] + } + } + }, + { + "id": "path-reverse", + "arguments": { + "compass": { + "source": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "target": "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1", + "max_hops": 8 + }, + "graphify": { + "source": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "target": "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-hop-limit", + "arguments": { + "compass": { + "source": "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1", + "target": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "max_hops": 0 + }, + "graphify": { + "source": "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter", + "target": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-missing-source", + "arguments": { + "compass": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "max_hops": 8 + }, + "graphify": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "unresolved" + } + }, + { + "id": "path-ambiguous-source", + "arguments": { + "compass": { + "source": "toJson", + "target": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "max_hops": 8 + }, + "graphify": { + "source": "toJson", + "target": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "ambiguous" + } + }, + { + "id": "path-disconnected", + "arguments": { + "compass": { + "source": "sha256:fd738ecee462cf8301dec9714d844e4833d5633c86ca80268f97478f1e48b2ec", + "target": "sha256:be5ecb530bbb853203267e0c7f289304e916e7659246abc657d5083579d51488", + "max_hops": 8 + }, + "graphify": { + "source": "extras_src_main_java_com_google_gson_extras_examples_rawcollections_rawcollectionsexample", + "target": "gson_src_main_java_com_google_gson_annotations_package_info", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "disconnected" + } + } + ] + }, + { + "name": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "sourceText": "function detectVersion(", + "graphSha256": { + "compass": "e33192de01f403cfde6c0030ba219c8e24fd26b561b984b6b710a46b3cc981e8", + "graphify": "c0f6d021616326da6b7daf5105b3b996c0bb4ba17c0bf54a062f2e692147b01a" + }, + "anchors": [ + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "text": "function detectVersion(", + "labels": [ + "detectVersion()" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 1, + "text": "", + "labels": [ + "from-json-schema", + "from-json-schema.ts" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 808, + "text": "function convertSchema(", + "labels": [ + "convertSchema()" + ] + }, + { + "file": ".claude/skills/triage/scripts/reindex.mjs", + "line": 17, + "text": "const dir = join(root, \".triage\", kind);" + }, + { + "file": ".configs/tsconfig.base.json", + "line": 1, + "text": "{" + } + ], + "pathQuestions": [ + { + "id": "path-forward", + "arguments": { + "compass": { + "source": "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843", + "target": "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "max_hops": 8 + }, + "graphify": { + "source": "packages_zod_src_v4_classic_from_json_schema_detectversion", + "target": "packages_zod_src_v4_classic_from_json_schema_convertschema", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 2, + "graphify": 2 + }, + "sourceWitness": { + "repository": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "question": "zod2-path-detectversion-convertschema", + "category": "file-containment", + "nodes": [ + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "text": "function detectVersion(", + "labels": [ + "detectVersion()" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 1, + "text": "", + "labels": [ + "from-json-schema", + "from-json-schema.ts" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 808, + "text": "function convertSchema(", + "labels": [ + "convertSchema()" + ] + } + ], + "steps": [ + { + "relations": [ + "contains" + ], + "direction": "reverse" + }, + { + "relations": [ + "contains" + ], + "direction": "forward" + } + ] + } + } + }, + { + "id": "path-reverse", + "arguments": { + "compass": { + "source": "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "target": "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843", + "max_hops": 8 + }, + "graphify": { + "source": "packages_zod_src_v4_classic_from_json_schema_convertschema", + "target": "packages_zod_src_v4_classic_from_json_schema_detectversion", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 2, + "graphify": 2 + } + } + }, + { + "id": "path-hop-limit", + "arguments": { + "compass": { + "source": "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843", + "target": "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "max_hops": 1 + }, + "graphify": { + "source": "packages_zod_src_v4_classic_from_json_schema_detectversion", + "target": "packages_zod_src_v4_classic_from_json_schema_convertschema", + "max_hops": 1, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 2, + "graphify": 2 + } + } + }, + { + "id": "path-missing-source", + "arguments": { + "compass": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "max_hops": 8 + }, + "graphify": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "packages_zod_src_v4_classic_from_json_schema_convertschema", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "unresolved" + } + }, + { + "id": "path-ambiguous-source", + "arguments": { + "compass": { + "source": "safeParse", + "target": "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "max_hops": 8 + }, + "graphify": { + "source": "safeParse", + "target": "packages_zod_src_v4_classic_from_json_schema_convertschema", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "ambiguous" + } + }, + { + "id": "path-disconnected", + "arguments": { + "compass": { + "source": "sha256:55feeed2916b9a3ce9249d83a36778c24d4f04a78c08c2358e3d4bb7ba36faf3", + "target": "sha256:d8b8d907733c4fd37c339650fac32f06d5e3974f2d9499985367c72e7f98100b", + "max_hops": 8 + }, + "graphify": { + "source": "claude_skills_triage_scripts_reindex_dir", + "target": "configs_tsconfig_base", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "disconnected" + } + } + ] + }, + { + "name": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "file": "src/routing/path_router.rs", + "line": 22, + "sourceText": "fn validate_path(", + "graphSha256": { + "compass": "2cc12d5060c83389c077e4eade892991fa8f7021b59d8f6f1dc98c14c2109812", + "graphify": "6ed791a771190e98ba2905e08480a9fa6ed733f46d56212ae3a4c90018d79b52" + }, + "anchors": [ + { + "file": "src/routing/path_router.rs", + "line": 22, + "text": "fn validate_path(", + "labels": [ + "validate_path()" + ] + }, + { + "file": "src/routing/path_router.rs", + "line": 36, + "text": "fn validate_v07_paths(", + "labels": [ + "validate_v07_paths()" + ] + }, + { + "file": "benches/benches.rs", + "line": 1, + "text": "#![allow(missing_docs)]" + }, + { + "file": "src/macros.rs", + "line": 1, + "text": "//! Internal macros" + } + ], + "pathQuestions": [ + { + "id": "path-forward", + "arguments": { + "compass": { + "source": "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb", + "target": "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "max_hops": 8 + }, + "graphify": { + "source": "src_routing_path_router_validate_path", + "target": "src_routing_path_router_validate_v07_paths", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "question": "axum2-path-validate-v07", + "category": "call", + "nodes": [ + { + "file": "src/routing/path_router.rs", + "line": 22, + "text": "fn validate_path(", + "labels": [ + "validate_path()" + ] + }, + { + "file": "src/routing/path_router.rs", + "line": 36, + "text": "fn validate_v07_paths(", + "labels": [ + "validate_v07_paths()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/routing/path_router.rs", + "line": 30, + "text": "validate_v07_paths(path)?" + } + } + ] + } + } + }, + { + "id": "path-reverse", + "arguments": { + "compass": { + "source": "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "target": "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb", + "max_hops": 8 + }, + "graphify": { + "source": "src_routing_path_router_validate_v07_paths", + "target": "src_routing_path_router_validate_path", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-hop-limit", + "arguments": { + "compass": { + "source": "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb", + "target": "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "max_hops": 0 + }, + "graphify": { + "source": "src_routing_path_router_validate_path", + "target": "src_routing_path_router_validate_v07_paths", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + } + } + }, + { + "id": "path-missing-source", + "arguments": { + "compass": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "max_hops": 8 + }, + "graphify": { + "source": "__compass_mcp_path_absent_7ed706b894__", + "target": "src_routing_path_router_validate_v07_paths", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "unresolved" + } + }, + { + "id": "path-ambiguous-source", + "arguments": { + "compass": { + "source": "with_state", + "target": "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "max_hops": 8 + }, + "graphify": { + "source": "with_state", + "target": "src_routing_path_router_validate_v07_paths", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "ambiguous" + } + }, + { + "id": "path-disconnected", + "arguments": { + "compass": { + "source": "sha256:c1950b3a15e552f198603684ad1c0fabbdac3da2a9b6fe67168907b334a25533", + "target": "sha256:f41133670252fffda7d266a65b5e0b9f2121957331570519ec0eec5bec04832e", + "max_hops": 8 + }, + "graphify": { + "source": "benches_benches", + "target": "src_macros", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "disconnected" + } + } + ] + } + ] +} diff --git a/benchmarks/agent_query/tests/test_mcp_paths.py b/benchmarks/agent_query/tests/test_mcp_paths.py new file mode 100644 index 000000000..78125cd54 --- /dev/null +++ b/benchmarks/agent_query/tests/test_mcp_paths.py @@ -0,0 +1,69 @@ +import unittest +from benchmarks.agent_query.mcp_compare import prepare_questions +from benchmarks.agent_query.mcp_path_audit import audit, parse_path + + +class PreparedPathTests(unittest.TestCase): + def question(self, **extra): + return {'pathQuestions':[{'id':'path', 'arguments':{ + 'graphify':{'source':'a','target':'b','max_hops':8,**extra}}}]} + + def test_direction_is_explicit_for_shared_navigation(self): + with self.assertRaises(ValueError):prepare_questions({},'graphify',self.question()) + result=prepare_questions({},'graphify',self.question(undirected=True)) + self.assertEqual(result[0][1],'shortest_path') + + def test_prepared_endpoint_is_not_changed_by_graph_lookup(self): + result=prepare_questions({},'graphify',self.question(undirected=True)) + self.assertEqual(result[0][2]['source'],'a') + + def test_invalid_hop_bound_and_unknown_argument_fail(self): + for extra in [{'undirected':True,'max_hops':True}, {'undirected':True,'other':'bad'}]: + with self.assertRaises(ValueError):prepare_questions({},'graphify',self.question(**extra)) + + +class PathAuditTests(unittest.TestCase): + def setUp(self): + self.graph={'nodes':[{'id':'a','name':'Alpha'},{'id':'b','name':'Beta'},{'id':'c','name':'Gamma'}], + 'links':[{'source':'a','target':'b','kind':'calls'}, + {'source':'a','target':'b','kind':'references'}]} + + def row(self,text,source='a',target='b',hops=8): + return {'repository':'fixture','tool':'compass','question':'q','text':text, + 'arguments':{'source':source,'target':target,'max_hops':hops},'executionSucceeded':True} + + def check(self,text,outcome='path',**kwargs): + return audit(self.row(text,**kwargs),self.graph,{'expected':{'outcome':outcome}}) + + def test_parallel_relations_and_reverse_navigation(self): + self.assertTrue(self.check('Shortest path (1 hops):\n Alpha --calls/references [EXTRACTED]--> Beta')['matched']) + self.assertTrue(self.check('Shortest path (1 hops):\n Beta <--calls-- Alpha',source='b',target='a')['matched']) + self.assertFalse(self.check('Shortest path (1 hops):\n Alpha <--calls-- Beta')['matched']) + + def test_endpoint_echo_and_unsupported_relation_are_not_paths(self): + self.assertFalse(self.check('Alpha Beta')['matched']) + self.assertFalse(self.check('Shortest path (1 hops):\n Alpha --imports--> Beta')['matched']) + with self.assertRaises(ValueError):parse_path('Shortest path (2 hops):\n Alpha --calls--> Beta') + + def test_missing_and_ambiguous_identity_are_distinct(self): + self.assertFalse(self.check('warning: ambiguous\nShortest path (1 hops):\n Alpha --calls--> Beta','ambiguous')['matched']) + self.assertTrue(self.check('Ambiguous endpoint: choose an ID','ambiguous')['matched']) + self.assertTrue(self.check("No node matching source 'missing' found.",'unresolved',source='missing')['matched']) + + def test_limit_does_not_prove_disconnection(self): + self.assertTrue(self.check('Path exceeds max_hops=0 (1 hops found).','depth-limit',hops=0)['matched']) + self.assertFalse(self.check("No path found between 'Alpha' and 'Beta'.",'depth-limit',hops=0)['matched']) + self.assertTrue(self.check("No path found between 'Alpha' and 'Gamma'.",'disconnected',target='c')['matched']) + + def test_colliding_display_label_does_not_identify_a_path(self): + self.graph['nodes'].append({'id':'d','name':'Beta'}) + checked=self.check('Shortest path (1 hops):\n Alpha --calls--> Beta') + self.assertFalse(checked['matched']) + self.assertIn('identity',checked['failure']) + + def test_graphify_direction_uses_semantic_markers(self): + graph={'nodes':[{'id':'a','label':'Alpha'},{'id':'b','label':'Beta'}], + 'links':[{'source':'b','target':'a','_src':'a','_tgt':'b','relation':'calls'}]} + row=self.row('Shortest path (1 hops):\n Alpha --calls [EXTRACTED]--> Beta') + row['tool']='graphify' + self.assertTrue(audit(row,graph,{'expected':{'outcome':'path'}})['matched']) From 9e21b0d1693984daf515a4cd4c66d1e9492af0a2 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 16:56:28 -0700 Subject: [PATCH 18/97] bench: add separate label-input MCP path comparison --- benchmarks/agent_query/mcp_path_audit.py | 7 +- .../agent_query/suite_mcp_path_labels.json | 916 ++++++++++++++++++ .../agent_query/tests/test_mcp_paths.py | 5 + 3 files changed, 925 insertions(+), 3 deletions(-) create mode 100644 benchmarks/agent_query/suite_mcp_path_labels.json diff --git a/benchmarks/agent_query/mcp_path_audit.py b/benchmarks/agent_query/mcp_path_audit.py index 1dec2d169..0630a1bf8 100644 --- a/benchmarks/agent_query/mcp_path_audit.py +++ b/benchmarks/agent_query/mcp_path_audit.py @@ -72,14 +72,15 @@ def audit(row, graph, question): result['matched'] = not positive and 'ambig' in text.lower() result['selectedDespiteAmbiguity'] = positive return result - distance = shortest_distance(graph, args['source'], args['target']) + source_id, target_id = question['expected'].get('endpointIds', {}).get(tool, [args['source'], args['target']]) + distance = shortest_distance(graph, source_id, target_id) result['storedShortestHops'] = distance if expected == 'depth-limit': result['matched'] = distance is not None and distance > args['max_hops'] and not positive and 'max_hops' in text return result if expected == 'disconnected': nodes = {n['id']: n for n in graph['nodes']} - expected_text = f"No path found between '{label(nodes[args['source']],tool)}' and '{label(nodes[args['target']],tool)}'." + expected_text = f"No path found between '{label(nodes[source_id],tool)}' and '{label(nodes[target_id],tool)}'." result['matched'] = distance is None and text == expected_text return result try: @@ -95,7 +96,7 @@ def audit(row, graph, question): ids = [n['id'] for n in nodes] result['nodeIds'] = ids failures = [] - if ids[0] != args['source'] or ids[-1] != args['target']: + if ids[0] != source_id or ids[-1] != target_id: failures.append('wrong endpoint identity') if len(steps) != distance or len(steps) > args['max_hops']: failures.append('path is not minimum-hop within the requested bound') diff --git a/benchmarks/agent_query/suite_mcp_path_labels.json b/benchmarks/agent_query/suite_mcp_path_labels.json new file mode 100644 index 000000000..7795f3aca --- /dev/null +++ b/benchmarks/agent_query/suite_mcp_path_labels.json @@ -0,0 +1,916 @@ +{ + "schema": "compass.mcp-comparison-inputs/1", + "scope": "Development label-input path arm added after the exact-ID diagnostic exposed Graphify endpoint substitution. Uses the same source-reviewed positive endpoint pairs and bounds, with each graph display label as input. This separates public label behavior from the exact-ID defect; not held-out evidence.", + "policy": { + "positive": "Verify actual ordered node chain, stored edge relation and displayed direction, and minimum hop distance. Ambiguous display labels leave identity unverified. Separately compare the reviewed source witness.", + "hop-limit": "No positive path beyond max_hops; an explicit bound outcome is not global disconnection.", + "missing": "Unknown endpoint must remain unresolved, not match a convenient fuzzy node.", + "ambiguous": "At least two exact terminal-label declarations match. Refuse to choose; a warning followed by a path is not preserved ambiguity.", + "disconnected": "A symmetrically selected source-anchored pair lies in different undirected components in each graph. Require no path; no graph modification or synthetic fixture.", + "bounds": "Same 60-second/16-MiB-RPC and 64-MiB-session limits. max_hops=8 for positives, ambiguity, missing, and disconnected; one fewer than the shorter stored distance for the bound case. Graphify gets undirected=true; Compass currently exposes undirected navigation only." + }, + "repositories": [ + { + "name": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "file": "command.go", + "line": 757, + "sourceText": "func (c *Command) Find(", + "graphSha256": { + "compass": "6f862024bd68b88b734a43421a21cc2074a94ab7d68cc8857d59609112c9c7db", + "graphify": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + "anchors": [ + { + "file": "command.go", + "line": 757, + "text": "func (c *Command) Find(", + "labels": [ + ".Find()" + ] + }, + { + "file": "args.go", + "line": 28, + "text": "func legacyArgs(", + "labels": [ + "legacyArgs()" + ] + } + ], + "pathQuestions": [ + { + "id": "path-label-forward", + "arguments": { + "compass": { + "source": ".Find()", + "target": "legacyArgs()", + "max_hops": 8 + }, + "graphify": { + "source": ".Find()", + "target": "legacyArgs()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "question": "cobra2-path-find-legacyargs", + "category": "call", + "nodes": [ + { + "file": "command.go", + "line": 757, + "text": "func (c *Command) Find(", + "labels": [ + ".Find()" + ] + }, + { + "file": "args.go", + "line": 28, + "text": "func legacyArgs(", + "labels": [ + "legacyArgs()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "command.go", + "line": 776, + "text": "legacyArgs(commandFound, stripFlags(a, commandFound))" + } + } + ] + }, + "endpointIds": { + "compass": [ + "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8", + "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5" + ], + "graphify": [ + "cobra_command_find", + "args_legacyargs" + ] + } + } + }, + { + "id": "path-label-reverse", + "arguments": { + "compass": { + "source": "legacyArgs()", + "target": ".Find()", + "max_hops": 8 + }, + "graphify": { + "source": "legacyArgs()", + "target": ".Find()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5", + "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8" + ], + "graphify": [ + "args_legacyargs", + "cobra_command_find" + ] + } + } + }, + { + "id": "path-label-hop-limit", + "arguments": { + "compass": { + "source": ".Find()", + "target": "legacyArgs()", + "max_hops": 0 + }, + "graphify": { + "source": ".Find()", + "target": "legacyArgs()", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:caa6cf6216cb2dae7b47b44982140343a1204532221f6c393aabb4db55e9e1d8", + "sha256:77f4b61d6489e81ebd6fa1c0b0cce4fe02802d648c0b872c3d0d7c4b970e8de5" + ], + "graphify": [ + "cobra_command_find", + "args_legacyargs" + ] + } + } + } + ] + }, + { + "name": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "file": "src/flask/app.py", + "line": 995, + "sourceText": "def full_dispatch_request(", + "graphSha256": { + "compass": "227fe2d1f74cf835caa3ec15897f05544832fbf7f394e1c02e5134101a07c3ad", + "graphify": "ca35435af286c5c565fc66f55de569c2e3235f75c902182290ee15603ff103cc" + }, + "anchors": [ + { + "file": "src/flask/app.py", + "line": 995, + "text": "def full_dispatch_request(", + "labels": [ + ".full_dispatch_request()" + ] + }, + { + "file": "src/flask/app.py", + "line": 1024, + "text": "def finalize_request(", + "labels": [ + ".finalize_request()" + ] + }, + { + "file": "docs/conf.py", + "line": 1, + "text": "import packaging.version" + }, + { + "file": "examples/celery/make_celery.py", + "line": 1, + "text": "from task_app import create_app" + } + ], + "pathQuestions": [ + { + "id": "path-label-forward", + "arguments": { + "compass": { + "source": ".full_dispatch_request()", + "target": ".finalize_request()", + "max_hops": 8 + }, + "graphify": { + "source": ".full_dispatch_request()", + "target": ".finalize_request()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "question": "flask2-path-fulldispatch-finalize", + "category": "call", + "nodes": [ + { + "file": "src/flask/app.py", + "line": 995, + "text": "def full_dispatch_request(", + "labels": [ + ".full_dispatch_request()" + ] + }, + { + "file": "src/flask/app.py", + "line": 1024, + "text": "def finalize_request(", + "labels": [ + ".finalize_request()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/flask/app.py", + "line": 1022, + "text": "self.finalize_request(ctx, rv)" + } + } + ] + }, + "endpointIds": { + "compass": [ + "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5", + "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf" + ], + "graphify": [ + "src_flask_app_flask_full_dispatch_request", + "src_flask_app_flask_finalize_request" + ] + } + } + }, + { + "id": "path-label-reverse", + "arguments": { + "compass": { + "source": ".finalize_request()", + "target": ".full_dispatch_request()", + "max_hops": 8 + }, + "graphify": { + "source": ".finalize_request()", + "target": ".full_dispatch_request()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf", + "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5" + ], + "graphify": [ + "src_flask_app_flask_finalize_request", + "src_flask_app_flask_full_dispatch_request" + ] + } + } + }, + { + "id": "path-label-hop-limit", + "arguments": { + "compass": { + "source": ".full_dispatch_request()", + "target": ".finalize_request()", + "max_hops": 0 + }, + "graphify": { + "source": ".full_dispatch_request()", + "target": ".finalize_request()", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:afb60b148d2acb75824b381e59be62fa72cdb6443730e29b942ce2d8a4ead0d5", + "sha256:4fee624450db1903a6292eeba05c2ae2932efb632a554d032f01926c6a909baf" + ], + "graphify": [ + "src_flask_app_flask_full_dispatch_request", + "src_flask_app_flask_finalize_request" + ] + } + } + } + ] + }, + { + "name": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "sourceText": "public JsonWriter newJsonWriter(", + "graphSha256": { + "compass": "5856ddc99ec4025c9e36d128783e6e7393f45af76f617caa38fd6938aca23ea7", + "graphify": "92471ab8f5bb5d34aedddd2263fa24a43ae4ed31b34d06c8d433fe0f0dada447" + }, + "anchors": [ + { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(", + "labels": [ + ".newJsonWriter()" + ] + }, + { + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "text": "public class JsonWriter", + "labels": [ + "JsonWriter" + ] + }, + { + "file": "extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java", + "line": 1, + "text": "/*" + }, + { + "file": "gson/src/main/java/com/google/gson/annotations/package-info.java", + "line": 1, + "text": "/*" + } + ], + "pathQuestions": [ + { + "id": "path-label-forward", + "arguments": { + "compass": { + "source": ".newJsonWriter()", + "target": "JsonWriter", + "max_hops": 8 + }, + "graphify": { + "source": ".newJsonWriter()", + "target": "JsonWriter", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "question": "gson2-path-newjsonwriter-jsonwriter", + "category": "construction-reference", + "nodes": [ + { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(", + "labels": [ + ".newJsonWriter()" + ] + }, + { + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "text": "public class JsonWriter", + "labels": [ + "JsonWriter" + ] + } + ], + "steps": [ + { + "relations": [ + "instantiates", + "references" + ], + "direction": "forward", + "sitesByRelation": { + "instantiates": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 801, + "text": "new JsonWriter(writer)" + }, + "references": { + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 797, + "text": "public JsonWriter newJsonWriter(" + } + } + } + ] + }, + "endpointIds": { + "compass": [ + "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1", + "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8" + ], + "graphify": [ + "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter", + "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter" + ] + } + } + }, + { + "id": "path-label-reverse", + "arguments": { + "compass": { + "source": "JsonWriter", + "target": ".newJsonWriter()", + "max_hops": 8 + }, + "graphify": { + "source": "JsonWriter", + "target": ".newJsonWriter()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1" + ], + "graphify": [ + "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter" + ] + } + } + }, + { + "id": "path-label-hop-limit", + "arguments": { + "compass": { + "source": ".newJsonWriter()", + "target": "JsonWriter", + "max_hops": 0 + }, + "graphify": { + "source": ".newJsonWriter()", + "target": "JsonWriter", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:64e3ea7fc1f10444bca6ebed48973575a302fb826ba05905c6fe904323930fb1", + "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8" + ], + "graphify": [ + "gson_src_main_java_com_google_gson_gson_gson_newjsonwriter", + "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter" + ] + } + } + } + ] + }, + { + "name": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "sourceText": "function detectVersion(", + "graphSha256": { + "compass": "e33192de01f403cfde6c0030ba219c8e24fd26b561b984b6b710a46b3cc981e8", + "graphify": "c0f6d021616326da6b7daf5105b3b996c0bb4ba17c0bf54a062f2e692147b01a" + }, + "anchors": [ + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "text": "function detectVersion(", + "labels": [ + "detectVersion()" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 1, + "text": "", + "labels": [ + "from-json-schema", + "from-json-schema.ts" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 808, + "text": "function convertSchema(", + "labels": [ + "convertSchema()" + ] + }, + { + "file": ".claude/skills/triage/scripts/reindex.mjs", + "line": 17, + "text": "const dir = join(root, \".triage\", kind);" + }, + { + "file": ".configs/tsconfig.base.json", + "line": 1, + "text": "{" + } + ], + "pathQuestions": [ + { + "id": "path-label-forward", + "arguments": { + "compass": { + "source": "detectVersion()", + "target": "convertSchema()", + "max_hops": 8 + }, + "graphify": { + "source": "detectVersion()", + "target": "convertSchema()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 2, + "graphify": 2 + }, + "sourceWitness": { + "repository": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "question": "zod2-path-detectversion-convertschema", + "category": "file-containment", + "nodes": [ + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 105, + "text": "function detectVersion(", + "labels": [ + "detectVersion()" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 1, + "text": "", + "labels": [ + "from-json-schema", + "from-json-schema.ts" + ] + }, + { + "file": "packages/zod/src/v4/classic/from-json-schema.ts", + "line": 808, + "text": "function convertSchema(", + "labels": [ + "convertSchema()" + ] + } + ], + "steps": [ + { + "relations": [ + "contains" + ], + "direction": "reverse" + }, + { + "relations": [ + "contains" + ], + "direction": "forward" + } + ] + }, + "endpointIds": { + "compass": [ + "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843", + "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674" + ], + "graphify": [ + "packages_zod_src_v4_classic_from_json_schema_detectversion", + "packages_zod_src_v4_classic_from_json_schema_convertschema" + ] + } + } + }, + { + "id": "path-label-reverse", + "arguments": { + "compass": { + "source": "convertSchema()", + "target": "detectVersion()", + "max_hops": 8 + }, + "graphify": { + "source": "convertSchema()", + "target": "detectVersion()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 2, + "graphify": 2 + }, + "endpointIds": { + "compass": [ + "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674", + "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843" + ], + "graphify": [ + "packages_zod_src_v4_classic_from_json_schema_convertschema", + "packages_zod_src_v4_classic_from_json_schema_detectversion" + ] + } + } + }, + { + "id": "path-label-hop-limit", + "arguments": { + "compass": { + "source": "detectVersion()", + "target": "convertSchema()", + "max_hops": 1 + }, + "graphify": { + "source": "detectVersion()", + "target": "convertSchema()", + "max_hops": 1, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 2, + "graphify": 2 + }, + "endpointIds": { + "compass": [ + "sha256:a2ac366671d620c9721b85a975ca31e89ac2d076ceeb83b2aa9071a6611f7843", + "sha256:c24727f300fe1110afad52b8dae8fa5921e84d5471f2e144ce6724af95d3f674" + ], + "graphify": [ + "packages_zod_src_v4_classic_from_json_schema_detectversion", + "packages_zod_src_v4_classic_from_json_schema_convertschema" + ] + } + } + } + ] + }, + { + "name": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "file": "src/routing/path_router.rs", + "line": 22, + "sourceText": "fn validate_path(", + "graphSha256": { + "compass": "2cc12d5060c83389c077e4eade892991fa8f7021b59d8f6f1dc98c14c2109812", + "graphify": "6ed791a771190e98ba2905e08480a9fa6ed733f46d56212ae3a4c90018d79b52" + }, + "anchors": [ + { + "file": "src/routing/path_router.rs", + "line": 22, + "text": "fn validate_path(", + "labels": [ + "validate_path()" + ] + }, + { + "file": "src/routing/path_router.rs", + "line": 36, + "text": "fn validate_v07_paths(", + "labels": [ + "validate_v07_paths()" + ] + }, + { + "file": "benches/benches.rs", + "line": 1, + "text": "#![allow(missing_docs)]" + }, + { + "file": "src/macros.rs", + "line": 1, + "text": "//! Internal macros" + } + ], + "pathQuestions": [ + { + "id": "path-label-forward", + "arguments": { + "compass": { + "source": "validate_path()", + "target": "validate_v07_paths()", + "max_hops": 8 + }, + "graphify": { + "source": "validate_path()", + "target": "validate_v07_paths()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "sourceWitness": { + "repository": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "question": "axum2-path-validate-v07", + "category": "call", + "nodes": [ + { + "file": "src/routing/path_router.rs", + "line": 22, + "text": "fn validate_path(", + "labels": [ + "validate_path()" + ] + }, + { + "file": "src/routing/path_router.rs", + "line": 36, + "text": "fn validate_v07_paths(", + "labels": [ + "validate_v07_paths()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/routing/path_router.rs", + "line": 30, + "text": "validate_v07_paths(path)?" + } + } + ] + }, + "endpointIds": { + "compass": [ + "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb", + "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b" + ], + "graphify": [ + "src_routing_path_router_validate_path", + "src_routing_path_router_validate_v07_paths" + ] + } + } + }, + { + "id": "path-label-reverse", + "arguments": { + "compass": { + "source": "validate_v07_paths()", + "target": "validate_path()", + "max_hops": 8 + }, + "graphify": { + "source": "validate_v07_paths()", + "target": "validate_path()", + "max_hops": 8, + "undirected": true + } + }, + "expected": { + "outcome": "path", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b", + "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb" + ], + "graphify": [ + "src_routing_path_router_validate_v07_paths", + "src_routing_path_router_validate_path" + ] + } + } + }, + { + "id": "path-label-hop-limit", + "arguments": { + "compass": { + "source": "validate_path()", + "target": "validate_v07_paths()", + "max_hops": 0 + }, + "graphify": { + "source": "validate_path()", + "target": "validate_v07_paths()", + "max_hops": 0, + "undirected": true + } + }, + "expected": { + "outcome": "depth-limit", + "hops": { + "compass": 1, + "graphify": 1 + }, + "endpointIds": { + "compass": [ + "sha256:cbc027447c34fee567bd17fffd9fcdf6375504bf7590e421c97bf1be9d0f8efb", + "sha256:85ede979be9c1d48d63310ebfc8ab0785c71b64b196ce83430ada74f3ad0654b" + ], + "graphify": [ + "src_routing_path_router_validate_path", + "src_routing_path_router_validate_v07_paths" + ] + } + } + } + ] + } + ] +} diff --git a/benchmarks/agent_query/tests/test_mcp_paths.py b/benchmarks/agent_query/tests/test_mcp_paths.py index 78125cd54..d4766fee2 100644 --- a/benchmarks/agent_query/tests/test_mcp_paths.py +++ b/benchmarks/agent_query/tests/test_mcp_paths.py @@ -67,3 +67,8 @@ def test_graphify_direction_uses_semantic_markers(self): row=self.row('Shortest path (1 hops):\n Alpha --calls [EXTRACTED]--> Beta') row['tool']='graphify' self.assertTrue(audit(row,graph,{'expected':{'outcome':'path'}})['matched']) + + def test_label_requests_keep_independent_expected_endpoint_ids(self): + row=self.row('Shortest path (1 hops):\n Alpha --calls--> Beta',source='Alpha',target='Beta') + question={'expected':{'outcome':'path','endpointIds':{'compass':['a','b']}}} + self.assertTrue(audit(row,self.graph,question)['matched']) From fb42bb5ecc8f9cbf15e63705e7629042b1d133bd Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 17:34:23 -0700 Subject: [PATCH 19/97] fix: preserve bounded MCP path identities and source anchors --- CHANGELOG.md | 5 + COMPATIBILITY.md | 21 + MIGRATION.md | 8 + benchmarks/agent_query/COVERAGE_PLAN.md | 19 + benchmarks/agent_query/README.md | 27 ++ benchmarks/agent_query/mcp_compare.py | 11 +- benchmarks/agent_query/mcp_path_audit.py | 59 ++- .../agent_query/tests/test_mcp_paths.py | 36 ++ crates/compass-mcp/src/lib.rs | 430 ++++++++++-------- crates/compass-mcp/tests/coverage_paths.rs | 12 +- crates/compass-query/src/lib.rs | 7 +- crates/compass-query/src/score.rs | 9 +- crates/compass-query/src/traversal.rs | 139 +++++- .../tests/bounded_path_oracle.rs | 75 ++- ...ode-graph-intelligence-audit-2026-09-26.md | 160 ++++++- docs/reference/outputs.md | 26 ++ 16 files changed, 829 insertions(+), 215 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57953193e..3b6f07f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Require unique exact endpoints for MCP paths and return ambiguity candidates + instead of choosing by score. Explore within the requested hop bound using + shared work limits, prefer structural relations among equal-hop paths, and + include exact node/edge identities in structured responses. + - Include exact node IDs and source locations in MCP hub results, with a versioned structured projection for follow-up navigation. Describe hubs as topology candidates rather than established design defects. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 3e8a5c6e2..026448030 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -94,6 +94,27 @@ history profiles, and cache identities. ## Evolving contracts +### MCP paths + +`shortest_path` resolves exact node IDs or normalized symbol/qualified names. +It no longer substitutes a scored fuzzy endpoint. Ambiguous matches return at +most 20 candidates, ordered by ID, with an omission count; callers choose an +exact ID before requesting a path. Both endpoints resolving to the same node +continues to produce a diagnostic rather than a positive path. + +Search remains undirected and minimum-hop. Among equal-hop routes it now uses +the query engine's structural relation costs and deterministic path keys. +`max_hops` is enforced during traversal, accepts 0–64, and defaults to 8. +The same 1,000,000-adjacency-entry and 16 MiB cumulative path-key budgets as +the CLI path engine apply. Exhaustion fails explicitly. A depth-limited miss +does not assert global disconnection or the length of an unsearched route. + +Uniquely resolved, distinct endpoints add `compass.mcp.path/1` in the existing +structured transport envelope. It distinguishes `found`, `depth_limit`, and +`disconnected` and retains exact ordered node/edge identities. Resolution +diagnostics retain their text form. Legacy text is still available, including +through the non-transport `invoke` helper. Graph schemas are unchanged. + ### MCP hub identities MCP `god_nodes` adds `structuredContent` using the existing diff --git a/MIGRATION.md b/MIGRATION.md index f7651fdae..d583b7b75 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,14 @@ layout remains visible and clearly owned. ## Query text and path resolution +For MCP `shortest_path`, replace partial keywords with exact IDs or complete +symbol/qualified names. Handle ambiguity candidates before retrying. Use +`structuredContent.result` (`compass.mcp.path/1`) for ordered identities and +statuses. `depth_limit` and work-limit errors are incomplete searches, not +proof of disconnection. `max_hops` is limited to 0–64. Equal-hop routes may +change because structural relation cost now breaks ties; the operation remains +undirected navigation. + MCP callers of `get_neighbors` must handle an `Ambiguous` candidate list and retry with a returned exact node ID. Earlier versions silently chose one match. Labeled community IDs are restored by automatic traversal-cache rebuilding; diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 170d73fdf..63980a7c6 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -73,6 +73,25 @@ Do not compare one tool's model-assisted output with the other's native output. ## Scoring and acceptance +### MCP path diagnostics + +`suite_mcp_paths.json` covers prepared exact-ID navigation, reverse traversal, +hop cutoffs, missing/ambiguous endpoints, and disconnected pairs. The initial +capture occurred before its planned registration commit; its archived +preregistration claim is superseded by the development designation in the +audit report. `suite_mcp_path_labels.json` is a separate post-diagnostic arm +using the same positive endpoints and bounds with each tool's display labels. +Graphify's public path input description advertises labels/keywords, so the +label arm is necessary context for the ID failures and must be reported. + +Both arms use undirected navigation with identical external bounds. Audit +actual ordered identities, relations, direction, minimum distance, and source +anchors; distinguish topology consistency from reviewed source-route evidence. +Do not substitute expected IDs for ambiguous returned labels. A depth-limited +miss remains incomplete for a global-disconnection question. These development +arms do not complete directed call-flow, representative edge precision, path +occurrence recall, or held-out confirmation requirements. + ### Hub navigation diagnostic The label-identity gap found in the first MCP run motivates a separate diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index dc39a270a..9d51b1bfc 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -268,3 +268,30 @@ Neighbor checks cover displayed direction/label/relation triples and report ambiguous labels separately. Hub checks recompute displayed degrees for uniquely identified labels; they do not prove complete ranking eligibility, source correctness, functional cohesion, or god-object design quality. + +### MCP path diagnostics + +Pass `--inputs benchmarks/agent_query/suite_mcp_paths.json` to the same collector +for 28 questions per tool: forward/reverse minimum-hop routes, hop bounds, +missing and ambiguous endpoints, and disconnected pairs. Use +`python3 -m benchmarks.agent_query.mcp_path_audit --run /path/to/new-mcp-run +--output /path/to/new-path-audit.json` to check actual ordered identities, +relations, directions, and minimum hops against the stored graphs. Structured +Compass results must also preserve node source anchors and edge identities. +Reviewed source-route witnesses are reported separately from graph consistency. + +Run `suite_mcp_path_labels.json` as a separate input for the same positive +endpoints and bounds using display labels (15 questions per tool). This arm +was added after the ID diagnostic exposed Graphify endpoint substitution; +Graphify's public tool describes label/keyword inputs. Report both arms, +including competitor wins. Neither is held-out evaluation. All comparisons +request undirected navigation explicitly for Graphify; these are not evidence +of directed call-flow quality. + +The first ID diagnostic was captured before its planned registration commit +because a preliminary test command failed. Its archived input incorrectly says +preregistered; the development designation in the current manifest and audit +report supersedes that claim. Preserve original artifacts. Disconnected pairs +include source files/modules as well as declarations. A search that reaches +its depth or work bound has not proved global disconnection and must remain an +incomplete answer to that question. diff --git a/benchmarks/agent_query/mcp_compare.py b/benchmarks/agent_query/mcp_compare.py index ab4cf7929..ff5826d32 100644 --- a/benchmarks/agent_query/mcp_compare.py +++ b/benchmarks/agent_query/mcp_compare.py @@ -1,4 +1,4 @@ -"""Capture preregistered, public MCP operations on existing paired graphs. +"""Capture prepared, public MCP operations on existing paired graphs. This collector does not synthesize tool answers. Input IDs/community IDs are prepared symmetrically from captured graphs; that preparation is not scored as @@ -24,8 +24,15 @@ def community(node, tool): def prepare_questions(graph, tool, witness): if 'pathQuestions' in witness: + if not isinstance(witness['pathQuestions'], list) or not 1 <= len(witness['pathQuestions']) <= 32: + raise ValueError('prepared path questions must contain 1 to 32 entries') queries = [] + identifiers = set() for question in witness['pathQuestions']: + identifier = question['id'] + if not isinstance(identifier, str) or not identifier or identifier in identifiers: + raise ValueError('prepared question IDs must be nonempty and unique') + identifiers.add(identifier) arguments = question['arguments'][tool] if set(arguments) - {'source', 'target', 'max_hops', 'undirected'}: raise ValueError('unexpected prepared path argument') @@ -35,6 +42,8 @@ def prepare_questions(graph, tool, witness): raise ValueError('invalid prepared hop bound') if tool == 'graphify' and arguments.get('undirected') is not True: raise ValueError('shared navigation tasks require explicit undirected Graphify search') + if tool == 'compass' and 'undirected' in arguments: + raise ValueError('Compass path comparison must use its advertised implicit undirected mode') queries.append((question['id'], 'shortest_path', arguments)) return queries matches = [n for n in graph['nodes'] if _node_anchor(n, tool)[:2] == (witness['file'], witness['line']) diff --git a/benchmarks/agent_query/mcp_path_audit.py b/benchmarks/agent_query/mcp_path_audit.py index 0630a1bf8..5ca63d51e 100644 --- a/benchmarks/agent_query/mcp_path_audit.py +++ b/benchmarks/agent_query/mcp_path_audit.py @@ -64,6 +64,8 @@ def audit(row, graph, question): 'expected': expected, 'matched': False, 'executionSucceeded': row['executionSucceeded']} if 'captureError' in row: result['failure'] = row['captureError']; return result + if not row['executionSucceeded']: + result['failure'] = 'RPC execution did not succeed'; return result positive = 'Shortest path (' in text if expected == 'unresolved': result['matched'] = not positive and 'No node matching source' in text and args['source'] in text @@ -73,26 +75,57 @@ def audit(row, graph, question): result['selectedDespiteAmbiguity'] = positive return result source_id, target_id = question['expected'].get('endpointIds', {}).get(tool, [args['source'], args['target']]) + structured = row.get('response', {}).get('result', {}).get('structuredContent') + payload = None + if structured is not None: + payload = structured.get('result', {}) + if structured.get('schema') != 'compass.mcp.tool-result/1' or payload.get('schema') != 'compass.mcp.path/1': + raise ValueError('unsupported structured path response') + if (payload.get('source'), payload.get('target'), payload.get('maxHops')) != (source_id, target_id, args['max_hops']): + result['failure'] = 'structured request identity or bound differs'; return result distance = shortest_distance(graph, source_id, target_id) result['storedShortestHops'] = distance if expected == 'depth-limit': result['matched'] = distance is not None and distance > args['max_hops'] and not positive and 'max_hops' in text + if payload is not None and payload.get('status') != 'depth_limit': + result['matched'] = False + result['failure'] = 'structured status does not establish the required depth limit' return result if expected == 'disconnected': nodes = {n['id']: n for n in graph['nodes']} expected_text = f"No path found between '{label(nodes[source_id],tool)}' and '{label(nodes[target_id],tool)}'." result['matched'] = distance is None and text == expected_text + if payload is not None and payload.get('status') != 'disconnected': + result['matched'] = False + result['failure'] = 'structured status does not establish global disconnection' return result try: labels, steps = parse_path(text) except ValueError as error: result['failure'] = str(error); return result - names = {} + names = {}; by_id = {} for node in graph['nodes']: names.setdefault(label(node, tool), []).append(node) - if any(len(names.get(name, [])) != 1 for name in labels): - result['failure'] = 'path contains an unverified display identity'; return result - nodes = [names[name][0] for name in labels] + by_id[node['id']] = node + if payload is not None: + records = payload.get('nodes', []) + if payload.get('status') != 'found' or payload.get('hops') != len(steps) or len(records) != len(labels): + result['failure'] = 'structured path shape differs from text'; return result + nodes = [] + for record, displayed in zip(records, labels): + node = by_id.get(record.get('id')) + if node is None or record.get('label') != label(node, tool): + result['failure'] = 'unknown or mislabeled structured node identity'; return result + if displayed != re.sub(r'[\x00-\x1f\x7f-\x9f]', '', label(node,tool))[:256]: + result['failure'] = 'display label differs from structured identity'; return result + if (record.get('sourceFile'), record.get('startLine')) != _node_anchor(node,tool)[:2]: + result['failure'] = 'structured source anchor differs from graph'; return result + nodes.append(node) + result['explicitIdentities'] = len(nodes) + else: + if any(len(names.get(name, [])) != 1 for name in labels): + result['failure'] = 'path contains an unverified display identity'; return result + nodes = [names[name][0] for name in labels] ids = [n['id'] for n in nodes] result['nodeIds'] = ids failures = [] @@ -100,17 +133,27 @@ def audit(row, graph, question): failures.append('wrong endpoint identity') if len(steps) != distance or len(steps) > args['max_hops']: failures.append('path is not minimum-hop within the requested bound') - for left, right, step in zip(ids, ids[1:], steps): + if payload is not None and len(payload.get('steps', [])) != len(steps): + result['failure'] = 'structured edge count differs'; return result + for position, (left, right, step) in enumerate(zip(ids, ids[1:], steps)): source, target = (left, right) if step['direction'] == 'forward' else (right, left) - relations = set() + relations = set(); edge_ids = set() for edge in graph['links']: a, b = edge['source'], edge['target'] if tool == 'graphify': a, b = edge.get('_src', a), edge.get('_tgt', b) if (a, b) == (source, target): - relations.add(edge.get('kind' if tool == 'compass' else 'relation', 'related')) + relations.add(edge.get('kind' if tool == 'compass' else 'relation') or 'related') + if (edge.get('kind' if tool == 'compass' else 'relation') or 'related') in step['relations']: + edge_ids.add(edge.get('id')) if not set(step['relations']) <= relations: failures.append('printed relation/direction lacks a stored witness') + if payload is not None: + record = payload['steps'][position] + if ((record.get('from'),record.get('to'),record.get('source'),record.get('target'),record.get('direction')) + != (left,right,source,target,step['direction']) or record.get('relation') not in step['relations'] + or record.get('edgeId') not in edge_ids): + failures.append('structured edge identity or direction differs from graph/text') result.update(matched=not failures, failures=failures, steps=steps) witness = question['expected'].get('sourceWitness') if witness: @@ -136,7 +179,7 @@ def main(args): spec = next(r for r in inputs['repositories'] if r['name'] == key[0]) question = next(q for q in spec['pathQuestions'] if q['id'] == row['question']) if row['arguments'] != question['arguments'][key[1]]: - raise ValueError('request arguments differ from preregistration') + raise ValueError('request arguments differ from captured inputs') checked = audit(row, graphs[key], question); results.append(checked) print(key, row['question'], checked['matched'], checked.get('failure', checked.get('failures', []))) report = {'scope': __doc__, 'runSha256': _sha256_file(args.run/'run.json'), diff --git a/benchmarks/agent_query/tests/test_mcp_paths.py b/benchmarks/agent_query/tests/test_mcp_paths.py index d4766fee2..47c7fbb54 100644 --- a/benchmarks/agent_query/tests/test_mcp_paths.py +++ b/benchmarks/agent_query/tests/test_mcp_paths.py @@ -21,6 +21,13 @@ def test_invalid_hop_bound_and_unknown_argument_fail(self): for extra in [{'undirected':True,'max_hops':True}, {'undirected':True,'other':'bad'}]: with self.assertRaises(ValueError):prepare_questions({},'graphify',self.question(**extra)) + def test_prepared_questions_have_a_fixed_count_bound_and_unique_ids(self): + witness=self.question(undirected=True) + witness['pathQuestions'] *= 2 + with self.assertRaises(ValueError):prepare_questions({},'graphify',witness) + witness['pathQuestions'] *= 20 + with self.assertRaises(ValueError):prepare_questions({},'graphify',witness) + class PathAuditTests(unittest.TestCase): def setUp(self): @@ -55,6 +62,23 @@ def test_limit_does_not_prove_disconnection(self): self.assertFalse(self.check("No path found between 'Alpha' and 'Beta'.",'depth-limit',hops=0)['matched']) self.assertTrue(self.check("No path found between 'Alpha' and 'Gamma'.",'disconnected',target='c')['matched']) + def test_failed_execution_cannot_pass_from_error_text(self): + row=self.row("No node matching source 'missing' found.",source='missing') + row['executionSucceeded']=False + self.assertFalse(audit(row,self.graph,{'expected':{'outcome':'unresolved'}})['matched']) + + def test_structured_negative_status_must_agree_with_text(self): + for outcome,text,target,hops,status in [ + ('depth-limit','No path found within max_hops=0.','b',0,'depth_limit'), + ('disconnected',"No path found between 'Alpha' and 'Gamma'.",'c',8,'disconnected')]: + row=self.row(text,target=target,hops=hops) + payload={'schema':'compass.mcp.path/1','source':'a','target':target,'maxHops':hops,'status':status} + row['response']={'result':{'structuredContent':{'schema':'compass.mcp.tool-result/1','result':payload}}} + question={'expected':{'outcome':outcome}} + self.assertTrue(audit(row,self.graph,question)['matched']) + payload['status']='found' + self.assertFalse(audit(row,self.graph,question)['matched']) + def test_colliding_display_label_does_not_identify_a_path(self): self.graph['nodes'].append({'id':'d','name':'Beta'}) checked=self.check('Shortest path (1 hops):\n Alpha --calls--> Beta') @@ -72,3 +96,15 @@ def test_label_requests_keep_independent_expected_endpoint_ids(self): row=self.row('Shortest path (1 hops):\n Alpha --calls--> Beta',source='Alpha',target='Beta') question={'expected':{'outcome':'path','endpointIds':{'compass':['a','b']}}} self.assertTrue(audit(row,self.graph,question)['matched']) + + def test_structured_ids_disambiguate_but_must_match_the_recorded_edges(self): + self.graph['nodes'].append({'id':'d','name':'Beta'}) + row=self.row('Shortest path (1 hops):\n Alpha --calls--> Beta') + payload={'schema':'compass.mcp.path/1','status':'found','source':'a','target':'b','maxHops':8,'hops':1, + 'nodes':[{'id':'a','label':'Alpha'},{'id':'b','label':'Beta'}], + 'steps':[{'from':'a','to':'b','source':'a','target':'b','direction':'forward','relation':'calls','edgeId':None}]} + row['response']={'result':{'structuredContent':{'schema':'compass.mcp.tool-result/1','result':payload}}} + question={'expected':{'outcome':'path'}} + self.assertTrue(audit(row,self.graph,question)['matched']) + payload['steps'][0]['target']='d' + self.assertFalse(audit(row,self.graph,question)['matched']) diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index f45a17d58..36552463d 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -5,7 +5,7 @@ mod transport; pub use transport::{HttpOptions, serve_http, serve_stdio, serve_stdio_configured}; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs; use std::fs::OpenOptions; use std::io::{Read as _, Write as _}; @@ -43,7 +43,8 @@ use compass_prs::{ fetch_prs, fetch_worktrees, format_prs_text, parse_ci, }; use compass_query::{ - TraversalMode, find_node, pick_scored_endpoint, query_graph_text, sanitize_label, score_nodes, + HopPathResult, TraversalMode, find_exact_nodes, find_node, query_graph_text, sanitize_label, + shortest_hop_path, }; use rmcp::model::{ CallToolRequestParams, CallToolResult, ContentBlock, ErrorData, Implementation, @@ -370,7 +371,7 @@ impl CompassMcp { self.invoke_result(name, &mut arguments) .map(|result| { // Keep this legacy text helper stable; MCP carries both projections. - if name == "god_nodes" { + if matches!(name, "god_nodes" | "shortest_path") { return result.text; } result @@ -588,6 +589,9 @@ impl CompassMcp { if name == "god_nodes" { return invoke_hub_tool(arguments, &context); } + if name == "shortest_path" { + return invoke_path_tool(arguments, &context); + } Ok(ToolInvocation { text: invoke_tool(name, arguments, &context).map_err(InvocationError::InvalidParams)?, structured_content: None, @@ -1377,8 +1381,8 @@ fn tool_specs() -> Vec { ), tool( "shortest_path", - "Find the shortest path between two concepts in the knowledge graph.", - json!({"type":"object","properties":{"source":{"type":"string","description":"Source concept label or keyword"},"target":{"type":"string","description":"Target concept label or keyword"},"max_hops":{"type":"integer","default":8,"description":"Maximum hops to consider"}},"required":["source","target"]}), + "Find a bounded minimum-hop undirected navigation path between exact node identities. Ambiguous names require disambiguation. Structured results retain node and edge identities; equal-hop routes prefer structural relations.", + json!({"type":"object","properties":{"source":{"type":"string","description":"Source exact node ID, symbol, or qualified name"},"target":{"type":"string","description":"Target exact node ID, symbol, or qualified name"},"max_hops":{"type":"integer","minimum":0,"maximum":64,"default":8,"description":"Maximum hops explored"}},"required":["source","target"]}), ), tool( "list_prs", @@ -2428,159 +2432,173 @@ fn python_percent(count: usize, total: usize) -> usize { } } +fn resolve_mcp_path_endpoint(graph: &Graph, query: &str, role: &str) -> Result { + let matches = find_exact_nodes(graph, query); + match matches.as_slice() { + [index] => Ok(*index), + [] => Err(format!( + "No node matching {role} '{}' found.", + sanitize_label(query) + )), + _ => { + let mut candidates = matches + .iter() + .map(|index| graph.node(*index)) + .collect::>(); + candidates.sort_by(|left, right| left.id.cmp(&right.id)); + let mut lines = vec![format!( + "Ambiguous {role}: {} matches {} nodes. Retry with an exact node ID.", + json!(query), + candidates.len() + )]; + lines.extend(candidates.iter().take(20).map(|node| { + format!( + " {} [{}] id: {}", + sanitize_label(node.label()), + sanitize_label(&node.string("source_file")), + json!(node.id) + ) + })); + if candidates.len() > 20 { + lines.push(format!( + "{} additional candidates omitted.", + candidates.len() - 20 + )); + } + Err(lines.join("\n")) + } + } +} + fn tool_shortest_path( arguments: &Map, context: &GraphContext, ) -> Result { + invoke_path_tool(arguments, context) + .map(|result| result.text) + .map_err(|error| error.to_string()) +} + +fn invoke_path_tool( + arguments: &Map, + context: &GraphContext, +) -> Result { let source_query = string_argument(arguments, "source")?; let target_query = string_argument(arguments, "target")?; - let source_scores = score_nodes( - &context.graph, - &source_query - .split_whitespace() - .map(str::to_lowercase) - .collect::>(), - false, - ); - let target_scores = score_nodes( - &context.graph, - &target_query - .split_whitespace() - .map(str::to_lowercase) - .collect::>(), - false, - ); - if source_scores.ranked.is_empty() { - return Ok(format!("No node matching source '{source_query}' found.")); - } - if target_scores.ranked.is_empty() { - return Ok(format!("No node matching target '{target_query}' found.")); - } - let source = pick_scored_endpoint(&context.graph, &source_scores.ranked, source_query); - let target = pick_scored_endpoint(&context.graph, &target_scores.ranked, target_query); - if source == target { - return Ok(format!( - "'{source_query}' and '{target_query}' both resolved to the same node '{}'. Use a more specific label or the exact node ID.", - context.graph.node(source).id - )); - } - let Some(path) = shortest_path(&context.graph, source, target) else { - return Ok(format!( - "No path found between '{}' and '{}'.", - context.graph.node(source).label(), - context.graph.node(target).label() - )); + let max_hops = match arguments.get("max_hops") { + None => 8, + Some(value) => value + .as_u64() + .or_else(|| value.as_str()?.parse().ok()) + .filter(|value| *value <= 64) + .ok_or_else(|| "max_hops must be between 0 and 64".to_owned())?, }; - let hops = path.len().saturating_sub(1); - let max_hops = - usize::try_from(integer_argument(arguments, "max_hops", 8).max(0)).unwrap_or_default(); - if hops > max_hops { - return Ok(format!( - "Path exceeds max_hops={max_hops} ({hops} hops found)." - )); - } - let mut warnings = Vec::new(); - ambiguity_warning("source", &source_scores.ranked, source, &mut warnings); - ambiguity_warning("target", &target_scores.ranked, target, &mut warnings); - let mut segments = vec![context.graph.node(path[0]).label().to_owned()]; - for pair in path.windows(2) { - let left = pair[0]; - let right = pair[1]; - if let Some(edge_index) = context.graph.edge_between(left, right) { - let edge = context.graph.edge(edge_index); - let confidence = edge.string("confidence"); - let suffix = if confidence.is_empty() { - String::new() - } else { - format!(" [{confidence}]") - }; - segments.push(format!( - "--{}{suffix}--> {}", - edge.string("relation"), - context.graph.node(right).label() - )); - } else if let Some(edge_index) = context.graph.edge_between(right, left) { - let edge = context.graph.edge(edge_index); - let confidence = edge.string("confidence"); - let suffix = if confidence.is_empty() { - String::new() - } else { - format!(" [{confidence}]") - }; - segments.push(format!( - "<--{}{suffix}-- {}", - edge.string("relation"), - context.graph.node(right).label() - )); + let max_hops = usize::try_from(max_hops).map_err(|error| error.to_string())?; + // Select and project from one full snapshot: the compact traversal cache + // omits source lines and edge IDs, including parallel-edge identities. + let mut document = context.document()?; + document.directed = true; + let graph = Graph::from_traversal_document(document) + .map_err(|error| InvocationError::Internal(error.to_string()))?; + let source = match resolve_mcp_path_endpoint(&graph, source_query, "source") { + Ok(index) => index, + Err(text) => { + return Ok(ToolInvocation { + text, + structured_content: None, + }); } - } - let prefix = if warnings.is_empty() { - String::new() - } else { - format!("{}\n", warnings.join("\n")) }; - Ok(format!( - "{prefix}Shortest path ({hops} hops):\n {}", - segments.join(" ") - )) -} - -fn ambiguity_warning( - name: &str, - scores: &[compass_query::ScoredNode], - chosen: NodeIndex, - warnings: &mut Vec, -) { - if scores.len() < 2 || scores[0].node != chosen || scores[0].score <= 0.0 { - return; - } - let top = scores[0].score; - let runner = scores[1].score; - if (top - runner) / top < 0.10 { - warnings.push(format!( - "warning: {name} match was ambiguous (top score {}, runner-up {})", - format_score(top), - format_score(runner) - )); - } -} - -fn format_score(value: f64) -> String { - if value.fract() == 0.0 { - format!("{value:.0}") - } else { - format!("{value:.6}") - .trim_end_matches('0') - .trim_end_matches('.') - .to_owned() + let target = match resolve_mcp_path_endpoint(&graph, target_query, "target") { + Ok(index) => index, + Err(text) => { + return Ok(ToolInvocation { + text, + structured_content: None, + }); + } + }; + if source == target { + return Ok(ToolInvocation { + text: format!( + "'{}' and '{}' both resolved to the same node {}. Use a more specific label or the exact node ID.", + sanitize_label(source_query), + sanitize_label(target_query), + json!(graph.node(source).id) + ), + structured_content: None, + }); } -} - -fn shortest_path(graph: &Graph, source: NodeIndex, target: NodeIndex) -> Option> { - let mut queue = VecDeque::from([source]); - let mut parent = HashMap::::new(); - parent.insert(source, source); - while let Some(node) = queue.pop_front() { - if node == target { - break; + let mut result = json!({"schema":"compass.mcp.path/1", "direction":"undirected", + "ranking":"hops-then-structural-cost", "maxHops":max_hops, + "source":graph.node(source).id, "target":graph.node(target).id}); + let text = match shortest_hop_path(&graph, source, target, max_hops)? { + HopPathResult::NoPath { + visited_nodes, + depth_limited, + } => { + result["status"] = json!(if depth_limited { + "depth_limit" + } else { + "disconnected" + }); + result["visitedNodes"] = json!(visited_nodes); + if depth_limited { + format!( + "No path found within max_hops={max_hops}; search stopped at the hop bound." + ) + } else { + format!( + "No path found between '{}' and '{}'.", + sanitize_label(graph.node(source).label()), + sanitize_label(graph.node(target).label()) + ) + } } - for neighbor in graph.successors(node).chain(graph.predecessors(node)) { - if let std::collections::hash_map::Entry::Vacant(entry) = parent.entry(neighbor) { - entry.insert(node); - queue.push_back(neighbor); + HopPathResult::Found { nodes, edges } => { + let hops = edges.len(); + result["status"] = json!("found"); + result["hops"] = json!(hops); + result["nodes"] = json!(nodes.iter().map(|index| { + let node = graph.node(*index); + json!({"id":node.id,"label":node.label(),"sourceFile":node.source_file(), + "startLine":node.unsigned("line_start"),"sourceLocation":node.string("source_location")}) + }).collect::>()); + let mut steps = Vec::with_capacity(edges.len()); + let mut segments = vec![sanitize_label(graph.node(source).label())]; + for (pair, edge_index) in nodes.windows(2).zip(edges) { + let edge = graph.edge(edge_index); + let forward = edge.source == graph.node(pair[0]).id; + let relation = edge.string("relation"); + let relation = if relation.is_empty() { + "related" + } else { + &relation + }; + let confidence = edge.string("confidence"); + let suffix = if confidence.is_empty() { + String::new() + } else { + format!(" [{}]", sanitize_label(&confidence)) + }; + let next = sanitize_label(graph.node(pair[1]).label()); + segments.push(if forward { + format!("--{}{suffix}--> {next}", sanitize_label(relation)) + } else { + format!("<--{}{suffix}-- {next}", sanitize_label(relation)) + }); + steps.push(json!({"from":graph.node(pair[0]).id,"to":graph.node(pair[1]).id, + "source":edge.source,"target":edge.target,"edgeId":edge.attributes.get("id"), + "relation":relation,"confidence":confidence,"direction":if forward {"forward"} else {"reverse"}})); } + result["steps"] = json!(steps); + format!("Shortest path ({hops} hops):\n {}", segments.join(" ")) } - } - if !parent.contains_key(&target) { - return None; - } - let mut path = vec![target]; - while path.last().copied() != Some(source) { - let next = parent.get(path.last()?).copied()?; - path.push(next); - } - path.reverse(); - Some(path) + }; + Ok(ToolInvocation { + text, + structured_content: Some(transport_envelope(result)?), + }) } fn tool_list_prs(arguments: &Map) -> Result { @@ -2934,6 +2952,89 @@ fn read_bounded_resource(path: &Path) -> Result { mod tests { use super::*; + #[test] + fn mcp_paths_require_unique_exact_endpoints() -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("graph.json"); + fs::write( + &path, + serde_json::to_vec(&json!({ + "directed":true,"multigraph":true, + "nodes":[ + {"id":"MiXeD","label":"run()","qualified_name":"First.run","source":{"file":"a.rs","startLine":9}}, + {"id":"b","label":"run()","qualified_name":"Second.run","source_file":"b.rs"}, + {"id":"target","label":"Finish","source_file":"target.rs"} + ], + "links":[ + {"id":"edge-b","source":"MiXeD","target":"target","relation":"calls"}, + {"id":"edge-a","source":"MiXeD","target":"target","relation":"calls"} + ] + }))?, + )?; + let server = CompassMcp::new(&path); + for (source, target, role) in [("run", "target", "source"), ("target", "run", "target")] { + let text = server.invoke( + "shortest_path", + Map::from_iter([ + ("source".into(), json!(source)), + ("target".into(), json!(target)), + ]), + ); + assert!(text.contains(&format!("Ambiguous {role}")), "{text}"); + assert!(text.contains("MiXeD") && text.contains("b.rs"), "{text}"); + assert!(!text.contains("Shortest path"), "{text}"); + } + for source in ["MiXeD", "First.run"] { + let text = server.invoke( + "shortest_path", + Map::from_iter([ + ("source".into(), json!(source)), + ("target".into(), json!("target")), + ]), + ); + assert!(text.contains("Shortest path (1 hops)"), "{text}"); + } + let missing = server.invoke( + "shortest_path", + Map::from_iter([ + ("source".into(), json!("ru")), + ("target".into(), json!("target")), + ]), + ); + assert!(missing.contains("No node matching source"), "{missing}"); + for (hops, status) in [(0, "depth_limit"), (1, "found")] { + let result = server + .invoke_result( + "shortest_path", + &mut Map::from_iter([ + ("source".into(), json!("MiXeD")), + ("target".into(), json!("target")), + ("max_hops".into(), json!(hops)), + ]), + ) + .map_err(|error| error.to_string())?; + let content = result.structured_content.ok_or("missing path identity")?; + assert_eq!(content["result"]["status"], status); + assert_eq!(content["result"]["source"], "MiXeD"); + if hops == 1 { + assert_eq!(content["result"]["nodes"][0]["id"], "MiXeD"); + assert_eq!(content["result"]["nodes"][0]["startLine"], 9); + assert_eq!(content["result"]["steps"][0]["target"], "target"); + assert_eq!(content["result"]["steps"][0]["edgeId"], "edge-a"); + } + } + let invalid = server.invoke_result( + "shortest_path", + &mut Map::from_iter([ + ("source".into(), json!("MiXeD")), + ("target".into(), json!("target")), + ("max_hops".into(), json!(65)), + ]), + ); + assert!(invalid.is_err()); + Ok(()) + } + #[test] fn hub_results_preserve_exact_identity_and_source_anchors() -> Result<(), Box> { @@ -3569,7 +3670,7 @@ mod tests { "shortest_path", json!({"source":"Alpha","target":"Gamma","max_hops":0}) ) - .contains("exceeds max_hops") + .contains("within max_hops") ); assert!( invoke("shortest_path", json!({"source":"Delta","target":"Alpha"})) @@ -3642,8 +3743,6 @@ mod tests { expand_home(Path::new("plain/path")), PathBuf::from("plain/path") ); - assert_eq!(format_score(2.0), "2"); - assert_eq!(format_score(1.234_567_89), "1.234568"); assert_eq!(python_percent(1, 3), 33); assert_eq!(python_percent(2, 3), 67); for (index, status) in [ @@ -3706,39 +3805,6 @@ mod tests { "Unknown tool: not-real" ); - let a = context.graph.node_index("a").ok_or("node a")?; - let b = context.graph.node_index("b").ok_or("node b")?; - let c = context.graph.node_index("c").ok_or("node c")?; - assert_eq!(shortest_path(&context.graph, a, a), Some(vec![a])); - assert_eq!(shortest_path(&context.graph, c, a), Some(vec![c, b, a])); - let mut warnings = Vec::new(); - ambiguity_warning( - "source", - &[ - compass_query::ScoredNode { - score: 10.0, - node: a, - }, - compass_query::ScoredNode { - score: 9.5, - node: b, - }, - ], - a, - &mut warnings, - ); - assert_eq!(warnings.len(), 1); - ambiguity_warning( - "source", - &[compass_query::ScoredNode { - score: 0.0, - node: a, - }], - a, - &mut warnings, - ); - assert_eq!(warnings.len(), 1); - let reverse = tool_shortest_path( json!({"source":"Tail","target":"Twin"}) .as_object() @@ -3746,7 +3812,7 @@ mod tests { &context, )?; assert!(reverse.contains("<--uses")); - assert!(reverse.contains("<----")); + assert!(reverse.contains("<--related--")); assert!(expand_home(Path::new("~/compass-cache")).ends_with("compass-cache")); fs::write(&graph_path, "not json")?; diff --git a/crates/compass-mcp/tests/coverage_paths.rs b/crates/compass-mcp/tests/coverage_paths.rs index 1f4fd4e1c..70f8e5ab7 100644 --- a/crates/compass-mcp/tests/coverage_paths.rs +++ b/crates/compass-mcp/tests/coverage_paths.rs @@ -174,7 +174,7 @@ fn tool_contract_and_all_local_tools_cover_success_and_validation_paths() ("max_hops", json!(0)) ]) ) - .contains("Path exceeds max_hops=0") + .contains("No path found within max_hops=0") ); assert!( server @@ -313,6 +313,16 @@ async fn in_memory_protocol_exercises_tool_and_resource_server_handlers() let structured = hubs.structured_content.ok_or("missing structured hubs")?; assert_eq!(structured["result"]["schema"], "compass.mcp.hubs/1"); assert!(structured["result"]["nodes"].as_array().is_some()); + let path = client + .call_tool( + CallToolRequestParams::new("shortest_path") + .with_arguments(args(&[("source", json!("a")), ("target", json!("b"))])), + ) + .await?; + let path_result = path.structured_content.ok_or("missing structured path")?; + assert_eq!(path_result["result"]["schema"], "compass.mcp.path/1"); + assert_eq!(path_result["result"]["nodes"][0]["id"], "a"); + assert_eq!(path_result["result"]["steps"][0]["target"], "b"); assert!( client .read_resource(ReadResourceRequestParams::new("compass://report")) diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index 56242a3e2..ea93295a0 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -61,7 +61,8 @@ pub use relevance::{ }; pub use score::{ ProfiledQueryScores, QueryScores, ScoredNode, TEXT_RANKER_BM25_V1, TEXT_RANKER_FULL_SCAN_V1, - TextRankProfile, find_node, pick_scored_endpoint, score_nodes, score_nodes_with_profile, + TextRankProfile, find_exact_nodes, find_node, pick_scored_endpoint, score_nodes, + score_nodes_with_profile, }; pub use telemetry::{ ProfiledCodeQueryResponse, QUERY_EXECUTION_PROFILE_V1, QueryExecutionProfile, @@ -74,10 +75,10 @@ pub use text_cursor::{ }; pub use traversal::{ DEFAULT_PATH_DEPTH_LIMIT, DEFAULT_TEXT_TOKEN_BUDGET, ExplainedSource, ExplanationSourceError, - ProfiledTextPageOptions, TextPageOptions, TextPaginationError, TraversalMode, + HopPathResult, ProfiledTextPageOptions, TextPageOptions, TextPaginationError, TraversalMode, explanation_source, query_graph_text, query_graph_text_page, query_graph_text_page_with_profile, render_explanation, render_explanation_page, - render_shortest_path, render_shortest_path_with_limit, + render_shortest_path, render_shortest_path_with_limit, shortest_hop_path, }; /// Return the canonical semantic-result digest for a typed code query. diff --git a/crates/compass-query/src/score.rs b/crates/compass-query/src/score.rs index 50e2cbeec..9d82cde73 100644 --- a/crates/compass-query/src/score.rs +++ b/crates/compass-query/src/score.rs @@ -498,8 +498,12 @@ pub fn find_node(graph: &Graph, label: &str) -> Vec { source_exact } -pub(crate) fn find_exact_nodes(graph: &Graph, label: &str) -> Vec { - if let Some(index) = graph.node_index(label.trim()) { +/// Resolve an exact ID or normalized symbol/qualified name without fuzzy fallback. +pub fn find_exact_nodes(graph: &Graph, label: &str) -> Vec { + if let Some(index) = graph + .node_index(label) + .or_else(|| graph.node_index(label.trim())) + { return vec![index]; } let term = search_tokens(label).join(" "); @@ -718,6 +722,7 @@ mod tests { let matches = super::find_node(&graph, query); assert_eq!(matches.len(), 1); assert_eq!(graph.node(matches[0]).id, query); + assert_eq!(super::find_exact_nodes(&graph, query), matches); } let padded = super::find_node(&graph, " A "); assert_eq!(padded.len(), 1); diff --git a/crates/compass-query/src/traversal.rs b/crates/compass-query/src/traversal.rs index 217f590fe..51d11999a 100644 --- a/crates/compass-query/src/traversal.rs +++ b/crates/compass-query/src/traversal.rs @@ -536,6 +536,54 @@ enum PathRanking { struct GraphPathResult { path: Option, visited_nodes: usize, + depth_limited: bool, +} + +/// A bounded, undirected minimum-hop search over exact graph node identities. +#[derive(Debug, PartialEq, Eq)] +pub enum HopPathResult { + Found { + nodes: Vec, + edges: Vec, + }, + NoPath { + visited_nodes: usize, + depth_limited: bool, + }, +} + +/// Minimize hops, then structural relation cost, then the existing stable path key. +/// Uses the same adjacency and allocation budgets as the public weighted path query. +pub fn shortest_hop_path( + graph: &Graph, + source: NodeIndex, + target: NodeIndex, + max_hops: usize, +) -> Result { + if source >= graph.node_count() || target >= graph.node_count() { + return Err("path endpoint index is outside the selected graph".to_owned()); + } + if max_hops > 64 { + return Err("max_hops must be between 0 and 64".to_owned()); + } + let result = ranked_path_undirected( + graph, + source, + target, + max_hops, + PathRanking::Hops, + PathSearchBudget::default(), + )?; + Ok(match result.path { + Some(path) => HopPathResult::Found { + nodes: path.nodes, + edges: path.edges, + }, + None => HopPathResult::NoPath { + visited_nodes: result.visited_nodes, + depth_limited: result.depth_limited, + }, + }) } struct WeightedGraphPath { @@ -547,6 +595,7 @@ struct WeightedGraphPath { // Depth-aware search keeps multiple arrivals per node. Bound both the graph // work and the cumulative string allocation used for deterministic tie keys. // Exhaustion is an error, never evidence that the endpoints are disconnected. +#[derive(Clone, Copy)] struct PathSearchBudget { adjacency_entries: usize, key_bytes: usize, @@ -562,7 +611,7 @@ impl Default for PathSearchBudget { } fn path_work_limit() -> String { - "path search work limit exceeded; reduce --max-depth or use a smaller graph".to_owned() + "path search work limit exceeded; reduce the hop bound or use a smaller graph".to_owned() } fn ranked_path_undirected( @@ -584,6 +633,7 @@ fn ranked_path_undirected( let mut predecessor = BTreeMap::::new(); let mut visited = BTreeSet::new(); let mut target_state = None; + let mut depth_limited = false; while let Some(Reverse((primary, secondary, path_key, node))) = queue.pop() { let (weight, hops) = match ranking { PathRanking::Weighted => (primary, secondary), @@ -606,6 +656,10 @@ fn ranked_path_undirected( break; } if usize::try_from(hops).unwrap_or(usize::MAX) >= max_depth { + // Conservative: an unexpanded boundary with incident edges cannot + // establish global disconnection, even if those edges form cycles. + depth_limited |= graph.outgoing_edges(node).next().is_some() + || graph.incoming_edges(node).next().is_some(); continue; } for (neighbor, edge_index, edge_weight, edge_key) in @@ -656,6 +710,7 @@ fn ranked_path_undirected( return Ok(GraphPathResult { path: None, visited_nodes: visited.len(), + depth_limited, }); }; let mut nodes = vec![target]; @@ -681,6 +736,7 @@ fn ranked_path_undirected( weight, }), visited_nodes: visited.len(), + depth_limited, }) } @@ -1613,6 +1669,68 @@ mod tests { use super::dfs; + #[test] + fn hop_paths_preserve_bounds_identity_and_structural_ties() + -> Result<(), Box> { + for reverse in [false, true] { + let mut links = vec![ + json!({"source":"s","target":"a","relation":"references"}), + json!({"source":"a","target":"t","relation":"references"}), + json!({"source":"s","target":"b","relation":"contains"}), + json!({"source":"b","target":"t","relation":"contains"}), + ]; + if reverse { + links.reverse(); + } + let graph = Graph::from_document(serde_json::from_value::(json!({ + "directed":true, + "nodes":[{"id":"s"},{"id":"a","label":"Middle"}, + {"id":"b","label":"Middle"},{"id":"t"},{"id":"isolated"}], + "links":links + }))?)?; + let s = graph.node_index("s").ok_or("s")?; + let b = graph.node_index("b").ok_or("b")?; + let t = graph.node_index("t").ok_or("t")?; + let isolated = graph.node_index("isolated").ok_or("isolated")?; + let super::HopPathResult::Found { nodes, edges } = + super::shortest_hop_path(&graph, s, t, 2)? + else { + return Err("missing path".into()); + }; + assert_eq!(nodes, vec![s, b, t]); + assert_eq!(edges.len(), 2); + assert!( + edges + .iter() + .all(|edge| graph.edge(*edge).string("relation") == "contains") + ); + assert_eq!( + super::shortest_hop_path(&graph, s, t, 0)?, + super::HopPathResult::NoPath { + visited_nodes: 1, + depth_limited: true + } + ); + assert!(matches!( + super::shortest_hop_path(&graph, s, t, 1)?, + super::HopPathResult::NoPath { + depth_limited: true, + .. + } + )); + assert!(matches!( + super::shortest_hop_path(&graph, s, isolated, 8)?, + super::HopPathResult::NoPath { + depth_limited: false, + .. + } + )); + assert!(super::shortest_hop_path(&graph, s, t, 65).is_err()); + assert!(super::shortest_hop_path(&graph, usize::MAX, t, 8).is_err()); + } + Ok(()) + } + #[test] fn path_work_exhaustion_is_an_error_not_a_disconnected_result() -> Result<(), Box> { @@ -1633,18 +1751,13 @@ mod tests { ..Default::default() }, ] { - let error = super::ranked_path_undirected( - &graph, - seed, - target, - 2, - super::PathRanking::Weighted, - budget, - ) - .err() - .ok_or("expected work-limit error")?; - assert!(error.contains("path search work limit exceeded")); - assert!(!error.contains("NO PATH FOUND")); + for ranking in [super::PathRanking::Weighted, super::PathRanking::Hops] { + let error = super::ranked_path_undirected(&graph, seed, target, 2, ranking, budget) + .err() + .ok_or("expected work-limit error")?; + assert!(error.contains("path search work limit exceeded")); + assert!(!error.contains("NO PATH FOUND")); + } } Ok(()) } diff --git a/crates/compass-query/tests/bounded_path_oracle.rs b/crates/compass-query/tests/bounded_path_oracle.rs index a58871ca9..0008582dc 100644 --- a/crates/compass-query/tests/bounded_path_oracle.rs +++ b/crates/compass-query/tests/bounded_path_oracle.rs @@ -5,7 +5,9 @@ use std::error::Error; use compass_model::code_graph::{EdgeKind, GraphDocument, NodeKind}; use compass_model::identity::edge_id; use compass_model::query_contract::{CodeQueryLimits, NodeTrailRequest}; -use compass_query::{open_with_document, render_shortest_path_with_limit}; +use compass_query::{ + HopPathResult, open_with_document, render_shortest_path_with_limit, shortest_hop_path, +}; const PAIRS: [(usize, usize); 6] = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]; type Matrix = [[Option; 4]; 4]; @@ -33,8 +35,28 @@ fn oracle(matrix: &Matrix, max_depth: usize) -> Option<(u32, usize)> { visit(matrix, 0, max_depth, 1) } +fn hop_oracle(matrix: &Matrix, max_depth: usize) -> Option<(usize, u32)> { + fn visit(matrix: &Matrix, node: usize, remaining: usize, visited: u8) -> Option<(usize, u32)> { + if node == 3 { + return Some((0, 0)); + } + if remaining == 0 { + return None; + } + (0..4) + .filter(|next| visited & (1 << next) == 0) + .filter_map(|next| { + let edge = matrix[node][next]?; + let (hops, cost) = visit(matrix, next, remaining - 1, visited | (1 << next))?; + Some((hops + 1, cost + edge)) + }) + .min() + } + visit(matrix, 0, max_depth, 1) +} + #[test] -fn both_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box> { +fn all_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box> { let directory = tempfile::tempdir()?; let graph_path = directory.path().join("graph.json"); support::write_graph(&graph_path)?; @@ -103,6 +125,55 @@ fn both_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box { + assert_eq!(nodes.first(), Some(&start), "{context}"); + assert_eq!(nodes.last(), Some(&end), "{context}"); + assert_eq!(nodes.len(), hops + 1, "{context}"); + assert_eq!(edges.len(), hops, "{context}"); + let mut actual_cost = 0; + for (pair, edge) in nodes.windows(2).zip(edges) { + let edge = legacy.edge(edge); + let weight = match edge.string("relation").as_str() { + "calls" => 1, + "references" => 4, + _ => return Err("unknown path relation".into()), + }; + let source = edge + .source + .strip_prefix("n:") + .ok_or("edge source")? + .parse::()?; + let target = edge + .target + .strip_prefix("n:") + .ok_or("edge target")? + .parse::()?; + assert_eq!(directed[source][target], Some(weight), "{context}"); + let endpoints = (&legacy.node(pair[0]).id, &legacy.node(pair[1]).id); + assert!( + endpoints == (&edge.source, &edge.target) + || endpoints == (&edge.target, &edge.source), + "{context}" + ); + actual_cost += weight; + } + assert_eq!(actual_cost, cost, "{context}"); + } + (None, HopPathResult::NoPath { depth_limited, .. }) => { + if !depth_limited { + assert!(hop_oracle(&undirected, 3).is_none(), "{context}"); + } + } + (expected, actual) => { + return Err(format!("{context}: expected {expected:?}, got {actual:?}").into()); + } + } let output = render_shortest_path_with_limit(&legacy, "n:0", "n:3", depth)?; if let Some((cost, hops)) = oracle(&undirected, depth) { assert!( diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index d2bc55053..957566f17 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -719,6 +719,159 @@ These diagnostics motivate role-aware explanations and independent edge review. They are not folded into the original graph-consistency scores, and do not establish that Compass already diagnoses god objects reliably. +## MCP path identity, bounds, and source-route diagnostics + +The next development arm uses the same retained Cobra (Go), Flask (Python), +Gson (Java), Zod (TypeScript), and Axum (Rust) graphs. There are 28 questions per +tool: five forward paths, five reverse paths, five one-hop-too-small bounds, +five nonexistent source IDs, four ambiguous source names, and four disconnected +pairs. Exact IDs are prepared symmetrically from source anchors and are not +scored as retrieval. Each side has the same external 60-second/16-MiB RPC and +64-MiB session bounds. Graphify receives `undirected=true`; Compass's public +MCP operation is undirected navigation. These results do not establish directed +call-flow accuracy. + +Disconnected pairs are selected from common source-anchored nodes in different +components in each graph. They include files/modules/configuration as well as +function declarations. Preparation examined at most 2,048 common anchors; +no Cobra pair was selected, which does not prove none exists. Positive source +witnesses reuse development witnesses reviewed after earlier path outputs. + +**Registration correction:** inputs were written before the first requests, +but a transient Python runner cleanup `PermissionError` prevented the intended +pre-execution commit. The initial capture was launched before that command +failure was noticed. Its retained manifest's preregistration claim is wrong; +this report and the current input scope supersede it. This is a development +diagnostic. The original log is `mcp-path-python-01.log`; the child process was +subsequently absent and the full 81-test rerun passed. The runner was not changed +for an unreproduced failure. + +### Baseline findings and the separate label arm + +All 56 requests execute in `mcp-path-paired-01`. The independent auditor checks +transcript/input/graph hashes, actual path bodies, exact endpoint identity, +minimum distance, each ordered edge's relation and direction, and source-route +witnesses separately. It never resolves an ambiguous displayed intermediate +node using the expected answer. + +| ID-input diagnostic | Baseline Compass | Graphify 0.9.67 | +| --- | ---: | ---: | +| Verifiable minimum-hop path | 8/10 | 0/10 | +| Explicit hop-bound outcome | 5/5 | 3/5 | +| Missing source stays unresolved | 5/5 | 0/5 | +| Ambiguous source is not selected | 0/4 | 0/4 | +| Correct global disconnection | 4/4 | 1/4 | + +Compass's two positive misses use a Zod intermediate label shared by several +nodes; the text does not identify the selected node. This does not prove the +route itself wrong. Graphify's exact-ID inputs frequently select other nodes: +Cobra `.Find()` becomes `Command`, Axum `validate_path` becomes `routing()`, +and some pairs resolve to the same node. Its missing-ID sentinel also resolves +to fuzzy candidates. These are strict identity diagnostics, not a general +claim about Graphify's advertised label/keyword interface. + +A separate label-input arm was committed before its requests, after the ID +failures were observed. It uses the same positive source endpoints and bounds, +with each tool's actual display labels: 15 questions per tool, all 30 executed. +**Graphify wins this baseline comparison:** 10/10 verifiable paths versus +Compass's 8/10; both give 5/5 explicit hop-limit outcomes. Graphify matches all +five reviewed source routes; Compass verifies four of five, with Zod's identity +unverified. This arm is development evidence, not held-out confirmation. +Artifacts are `mcp-path-labels-01` and `mcp-path-labels-audit-01.json`. + +### Production corrections and the intermediate failure + +MCP path endpoints now require an exact ID or uniquely matching normalized +symbol/qualified name. Ambiguity returns stable candidates and exact IDs; +missing names remain unresolved. The public hop bound is validated from zero +to 64. Search uses the query crate's shared bounded engine, minimizing hops, +then structural relation cost, then a stable tie key. Its one-million-adjacency +and 16-MiB cumulative path-key budgets fail explicitly. Reaching a depth +frontier is distinguished from proving disconnection; conservative depth +reports may remain incomplete even when the full stored graph is disconnected. + +The versioned `compass.mcp.path/1` structured result preserves ordered node IDs, +source anchors, and selected edges with their stored and traversal directions. +Legacy text remains available. Equal-hop routes can change under the structural +tie rule, and callers relying on fuzzy endpoint selection must migrate; the +compatibility reference and migration guide document both changes. + +The first corrected binary (`3fc64bc00f1f47e36fb2158c9dfbfb360cf81915c6b81f588c84f5f29b023ed1`) +failed every positive structured-anchor check: compact traversal nodes omit +numeric source lines, so projection emitted null. Both `*-02` captures and +audits preserve this failure. Projection now reads the full stored node +records, as hub projection does, and a native regression requires line 9 to +survive actual structured invocation. The oracle was not relaxed. + +The intermediate ID run already refused all four ambiguous endpoints. It +reported global disconnection for only two of four disconnected pairs; Gson +and Axum reached the depth bound. Those two remain incomplete answers rather +than being credited as proven disconnections. + +The next frozen binary (`78e2a3528ef864937b7160fb523826b621218e05e14ccad51faba6dc8caf1082`) +restored all node source anchors, but `*-03` audits exposed null edge IDs: +the compact cache omits these too. All positive results therefore still failed +the structured-edge checks. The implementation now selects and renders paths +from one full bounded document snapshot, retaining parallel-edge IDs during +the search itself. It does not attach an arbitrary full-record edge after +traversing a lossy projection. The native regression uses two parallel calls +and requires the stable selected ID (`edge-a`), as well as the source line. +This full-snapshot load can cost more than compact traversal; no timing or +memory improvement is claimed. + +### Final path replay at this checkpoint + +The frozen executable in `mcp-path-identities-provenance` has SHA256 +`b16d7f755a81bf68110737a445701158ee541a4cc0f070b9e4e800cff693c988`. +Its base commit, full source patch, and changed-file hashes are retained. +`mcp-path-paired-04` completes all 56 RPCs and `mcp-path-labels-04` completes +all 30. Corresponding `mcp-path-audit-04.json` and +`mcp-path-labels-audit-04.json` validate raw transcripts and stored graph +identities. The current auditor also rechecks both baseline captures in +`mcp-path-baseline-current-audit.json` and +`mcp-path-labels-baseline-current-audit.json`; all 86 original outcomes remain +unchanged. Errors cannot pass from error text, and structured negative statuses +must agree with the required outcome. + +| ID-input diagnostic | Baseline Compass | Corrected Compass | Graphify | +| --- | ---: | ---: | ---: | +| Verifiable minimum-hop path | 8/10 | 10/10 | 0/10 | +| Explicit hop-bound outcome | 5/5 | 5/5 | 3/5 | +| Missing source stays unresolved | 5/5 | 5/5 | 0/5 | +| Ambiguous source is not selected | 0/4 | 4/4 | 0/4 | +| Correct global disconnection | 4/4 | 2/4 | 1/4 | + +The corrected label arm **ties Graphify**: each returns 10/10 verifiable +minimum-hop paths and 5/5 hop-bound outcomes. Each matches all five reviewed +source routes. Compass also matches those five routes in the ID arm; Graphify +matches none of the five with ID inputs. Failure to establish identity remains +unverified source-route evidence, not evidence that every underlying edge is +wrong. These short selected navigation routes do not establish representative +path precision, source occurrence recall, execution feasibility, or broad +superiority. + +Compass's two lost global-disconnection completions are explicit depth-limit +outcomes for Gson and Axum at eight hops. The original implementation searched +the whole component before applying the requested hop limit. The corrected +one stops at the bound; neither this report nor the auditor credits those +incomplete answers as disconnections. Retain that completion tradeoff alongside +the identity and ambiguity improvements. + +Verification: 1,212 native tests pass, zero fail, two are ignored. This includes +workspace library/binary tests, CLI query/product contracts, output agent-query +contracts, MCP coverage, typed traversal, and the three-engine independent +path oracle (729 four-node graphs × three depth bounds × three engines = +6,561 queries). The small oracle covers hop/cost optimality and actual edge +chains; it does not establish large-graph performance or source extraction +accuracy. Workspace and changed integration-target Clippy pass with warnings +denied. All 86 Python benchmark tests, formatting, diff checks, and the product +boundary gate pass. Logs are `mcp-path-corrections-tests-04.log`, +`mcp-path-corrections-clippy-04.log`, and `mcp-path-python-07.log`. +The test build emits existing core `unused_mut` and macOS linker unwind +warnings. Full extraction fixture qualification remains the earlier receiver +checkpoint: this correction changes query/MCP behavior on retained graphs, +not extraction. Concurrent debug replays are not timing evidence. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact @@ -728,9 +881,10 @@ do not establish that Compass already diagnoses god objects reliably. judgments, including containment-dominated modules and generic reference targets. Evaluate cluster responsibilities and cross-community connections separately from graph consistency. -3. Add independent edge/path judgments: ordered adjacent edges, relation kinds, - traversal direction, source occurrences, ambiguity, unreachable nodes, and - bound exhaustion. A negative or limit outcome must never count as a path. +3. Extend the development navigation-path judgments to directed call paths, + longer walks, parallel source occurrences, broader ambiguity/unreachable + cases, and real-repository work exhaustion. A negative or limit outcome + must never count as a path or proof of global disconnection. 4. Use held-out repositories/questions and publish all failures, including competitor wins. Separate extraction gaps, resolution gaps, retrieval gaps, rendering gaps and oracle mistakes using actual source evidence. diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 5fd507941..7dc048008 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -822,6 +822,32 @@ When exact automation is required, use: - diff JSON; - direct graph JSON. +### MCP navigation paths + +After uniquely resolving two distinct endpoints, MCP `shortest_path` adds +`structuredContent.result` with schema `compass.mcp.path/1` inside the existing +`compass.mcp.tool-result/1` transport envelope. Common fields are `source` and +`target` (exact IDs), `direction: "undirected"`, `maxHops`, and +`ranking: "hops-then-structural-cost"`. + +- `status: "found"` adds `hops`, ordered `nodes`, and ordered `steps`. + Nodes carry `id`, `label`, `sourceFile`, `startLine`, and `sourceLocation`. + Each step retains traversal `from`/`to`, stored `source`/`target`, `edgeId` + (null for legacy edges without an ID), `relation`, `confidence`, and + `direction` (`forward` or `reverse`). An absent relation is displayed as + `related`, not invented as a call. +- `status: "depth_limit"` means no path was found before the hop bound left + part of the search unexpanded. `visitedNodes` reports explored identities. +- `status: "disconnected"` means the reachable component was exhausted without + reaching the target; it also includes `visitedNodes`. + +Unresolved, ambiguous, and same-node endpoint diagnostics retain their text +form. No positive path is produced by guessing an endpoint. Work or transport +exhaustion is an explicit error, not a disconnected result. Numeric source +fields and source files can be null; absent textual source locations are empty. +This tool explores undirected graph navigation across stored relation kinds; +it does not establish directed call flow or execution feasibility. + ### MCP hub results MCP `god_nodes` returns human-readable text and a structured projection in From b57f4361d8cbb9b94a248b0d8b2f433af0383a5c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 17:50:28 -0700 Subject: [PATCH 20/97] bench: audit hub source roles and connectivity explanations --- benchmarks/agent_query/COVERAGE_PLAN.md | 18 + benchmarks/agent_query/README.md | 16 + benchmarks/agent_query/hub_evidence_audit.py | 175 +++ benchmarks/agent_query/hub_role_reviews.json | 1099 +++++++++++++++++ .../agent_query/tests/test_hub_evidence.py | 86 ++ 5 files changed, 1394 insertions(+) create mode 100644 benchmarks/agent_query/hub_evidence_audit.py create mode 100644 benchmarks/agent_query/hub_role_reviews.json create mode 100644 benchmarks/agent_query/tests/test_hub_evidence.py diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 63980a7c6..708481062 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -73,6 +73,24 @@ Do not compare one tool's model-assisted output with the other's native output. ## Scoring and acceptance +### Hub explanation and source-role review + +The post-output `hub_role_reviews.json` census covers all original fifty hub +entries per tool. Manually assign declaration/container roles only for exact +returned identities or globally unique displayed labels. Preserve unidentified +entries as unknown; do not choose the candidate that fits its degree. Verify +the pinned source commit, whole-file hash, exact anchor, and excerpt. These are +source-role descriptions, not god-object labels or a representative precision +sample, and each tool returns a different set. + +For the connectivity improvement, independently recompute each returned hub's +incident record total, self-loops, and bounded per-relation direction counts. +Distinguish these from distinct-pair ranking degree. Keep undirected artifacts +undirected. Check both structured values and the matching text block. An absent +summary is unavailable in that response, not an incorrect answer; neither a +neighbor follow-up workflow nor Graphify's separate CLI is excluded by this +finding. Do not turn summary availability into a cross-tool accuracy score. + ### MCP path diagnostics `suite_mcp_paths.json` covers prepared exact-ID navigation, reverse traversal, diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 9d51b1bfc..a21a4cd5e 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -269,6 +269,22 @@ ambiguous labels separately. Hub checks recompute displayed degrees for uniquely identified labels; they do not prove complete ranking eligibility, source correctness, functional cohesion, or god-object design quality. +### Hub explanation and source-role diagnostics + +`hub_evidence_audit.py --run /path/to/mcp-capture --output /path/to/new-audit.json` +checks each returned hub's optional connectivity summary against its own graph. +It distinguishes incident records, ranking degree, direction, self-loops, and +bounded relation rows. Absent summaries are unavailable, not incorrect answers; +this diagnostic does not measure a neighbor/CLI follow-up workflow. It must not +be used as a cross-tool precision score on the different returned hub sets. + +The post-output `hub_role_reviews.json` records manual declaration-role reviews +of the original MCP panel, preserving the 13 ambiguous Graphify identities as +unknown. Pass `--reviews benchmarks/agent_query/hub_role_reviews.json` only with +the original digest-matched capture. The auditor checks pinned source commits, +whole-file hashes, exact anchors, and excerpts. This validates the review's +source provenance; it does not automate semantic role or design-quality judgment. + ### MCP path diagnostics Pass `--inputs benchmarks/agent_query/suite_mcp_paths.json` to the same collector diff --git a/benchmarks/agent_query/hub_evidence_audit.py b/benchmarks/agent_query/hub_evidence_audit.py new file mode 100644 index 000000000..6481cbca1 --- /dev/null +++ b/benchmarks/agent_query/hub_evidence_audit.py @@ -0,0 +1,175 @@ +"""Audit hub connectivity summaries and optional post-output source-role reviews. + +Counts describe each tool's own stored graph. Missing summaries are unavailable, +not incorrect answers; follow-up/CLI workflows are not scored by this diagnostic. +Source roles do not establish cohesion, responsibility count, or god-object defects. +""" +import argparse +from collections import Counter +import json +from pathlib import Path +import re +from types import SimpleNamespace + +from benchmarks.agent_query.mcp_audit import audit, main as verify_capture +from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES +from benchmarks.agent_query.runner import _node_anchor, _sha256_file, _verify_source, load_suite + + +def connectivity(graph, tool, identifier): + nodes = {n['id'] for n in graph['nodes']} + if identifier not in nodes: + raise ValueError('unknown hub identity') + directed = graph.get('directed', False) + relations = {} + loops = 0 + for edge in graph['links']: + source, target = edge['source'], edge['target'] + if source not in nodes or target not in nodes or identifier not in (source, target): + continue + relation = edge.get('kind' if tool == 'compass' else 'relation', '') + row = relations.setdefault(relation, dict(relation=relation, edgeRecords=0, + incomingRecords=0, outgoingRecords=0, undirectedRecords=0)) + row['edgeRecords'] += 1 + loops += source == target + if directed: + row['incomingRecords'] += target == identifier + row['outgoingRecords'] += source == identifier + else: + row['undirectedRecords'] += 1 + ordered = sorted(relations.values(), key=lambda row: (-row['edgeRecords'], row['relation'])) + return dict(schema='compass.hub-connectivity/1', directed=directed, + edgeRecords=sum(row['edgeRecords'] for row in ordered), selfLoopRecords=loops, + relations=ordered[:16], omittedRelationKinds=max(0, len(ordered)-16), + omittedRelationRecords=sum(row['edgeRecords'] for row in ordered[16:])) + + +def check_summary(actual, expected): + # Canonical JSON distinguishes boolean counters from integers, unlike ==. + return json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def check_request(request, row): + expected = dict(jsonrpc='2.0', method='tools/call', id=row['response']['id'], + params=dict(name='god_nodes', arguments=row['arguments'])) + if not check_summary(request, expected): + raise ValueError('hub request differs from raw transcript') + + +def check_text_rows(text, rank, expected): + headers = list(re.finditer(r'^ (\d+)\. .* - \d+ edges$', text, re.M)) + matches = [i for i, header in enumerate(headers) if int(header[1]) == rank] + if len(matches) != 1: + return False + position = matches[0] + end = headers[position+1].start() if position+1 < len(headers) else len(text) + block = text[headers[position].end():end].splitlines() + wanted = [f" relation {json.dumps(r['relation'], ensure_ascii=False)}: incoming {r['incomingRecords']}, outgoing {r['outgoingRecords']}, undirected {r['undirectedRecords']}" for r in expected['relations']] + actual = [line for line in block if line.startswith(' relation ')] + totals = f" | incident records: {expected['edgeRecords']} | self-loops: {expected['selfLoopRecords']}" + omitted = [line for line in block if ' additional relation kinds (' in line] + omissions = ([f" {expected['omittedRelationKinds']} additional relation kinds ({expected['omittedRelationRecords']} records) omitted"] + if expected['omittedRelationKinds'] else []) + return actual == wanted and omitted == omissions and any(line.startswith(' kind: ') and line.endswith(totals) for line in block) + + +def check_review(review, node, tool, root): + file, line, _ = _node_anchor(node, tool) + if (review['id'], review['file'], review['line']) != (node['id'], file, line): + raise ValueError('review identity/source differs from captured graph') + path = (root/file).resolve() + path.relative_to(root.resolve()) + data = read_bounded(path, 4 * 1024 * 1024) + if _sha256_file(path) != review['sourceFileSha256']: + raise ValueError('reviewed source file changed') + lines = data.decode().splitlines() + if type(line) is not int or not 1 <= line <= len(lines): + raise ValueError('invalid review source line') + if '\n'.join(lines[line-1:line+3]) != review['sourceText']: + raise ValueError('review excerpt differs from source') + + +def main(args): + verify_capture(SimpleNamespace(run=args.run, output=args.output.with_suffix('.capture.json'))) + run = json.loads(read_bounded(args.run/'run.json')) + source_run = Path(run['sourceRun']) + source = json.loads(read_bounded(source_run/'run.json')) + suite = load_suite(source_run/'suite.toml') + if suite.digest != source['suiteDigest']: + raise ValueError('source suite changed') + reviews = None + if args.reviews: + manifest = json.loads(read_bounded(args.reviews)) + if manifest['schema'] != 'compass.hub-source-review/1' or manifest['inputRunSha256'] != _sha256_file(args.run/'run.json'): + raise ValueError('reviews do not identify this source capture') + reviews = {(r['repository'], r['tool'], r['rank']): r for r in manifest['reviews']} + if len(reviews) != len(manifest['reviews']) or len(reviews) != 100: + raise ValueError('review denominator must contain 100 unique hub entries') + results = [] + for row in run['results']: + if row['question'] != 'hubs': + continue + name, tool = row['repository'], row['tool'] + if row['executionSucceeded']: + identity = row['response']['id'] + request = json.loads(read_bounded(args.run/'raw'/name/tool/f'{identity:02}.request.json')) + check_request(request, row) + repo = next(r for r in source['repositories'] if r['repository'] == name) + graph = json.loads(read_bounded(Path(repo[tool+'Graph']), MAX_GRAPH_BYTES)) + nodes = {n['id']: n for n in graph['nodes']} + if reviews is not None: + pinned = next(r for r in suite.repositories if r.name == name) + _verify_source(pinned, Path(repo['source'])) + expected_source = next(r for r in manifest['sources'] if r['repository'] == name) + if any(repo[k] != expected_source[k] for k in ['commit', 'compassGraphSha256', 'graphifyGraphSha256']): + raise ValueError('review source provenance differs') + checked = audit(row, graph) + entries = row.get('response', {}).get('result', {}).get('structuredContent', {}).get('result', {}).get('nodes', []) + by_rank = {r['rank']: r for r in entries} + hubs = checked.get('hubs', []) + if not row['executionSucceeded'] or len(hubs) != 10: + raise ValueError('capture does not supply the full ten-hub denominator') + for hub in hubs: + result = dict(repository=name, tool=tool, rank=hub['rank'], + label=hub['label'], identityKnown='id' in hub, summaryAvailable=False) + if 'id' in hub: + expected = connectivity(graph, tool, hub['id']) + result.update(id=hub['id'], expected=expected) + actual = by_rank.get(hub['rank'], {}).get('connectivity') + if actual is not None: + result.update(summaryAvailable=True, summaryMatches=check_summary(actual, expected)) + result['textRowsMatch'] = check_text_rows(row['text'], hub['rank'], expected) + if reviews is not None: + review = reviews[name, tool, hub['rank']] + if 'id' in hub: + if review['role'] not in manifest['roles']: + raise ValueError('missing reviewed source role') + check_review(review, nodes[hub['id']], tool, Path(repo['source'])) + result.update(reviewedRole=review['role'], sourceVerified=True) + elif review['role'] is not None: + raise ValueError('ambiguous hub cannot borrow an oracle identity') + else: + result['reviewedRole'] = 'unverified-identity' + results.append(result) + if len(results) != 100: + raise ValueError('expected fifty returned hubs per tool') + report = dict(scope=__doc__, runSha256=_sha256_file(args.run/'run.json'), + auditorSha256=_sha256_file(Path(__file__)), results=results) + if reviews is not None: + report['reviewsSha256'] = _sha256_file(args.reviews) + with args.output.open('x') as stream: + json.dump(report, stream, indent=2) + for tool in ['compass', 'graphify']: + rows = [r for r in results if r['tool'] == tool] + print(tool, dict(total=len(rows), identityKnown=sum(r['identityKnown'] for r in rows), + summaryAvailable=sum(r['summaryAvailable'] for r in rows), + summaryMatches=sum(r.get('summaryMatches', False) for r in rows), + roles=dict(Counter(r.get('reviewedRole', 'not-reviewed') for r in rows)))) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--run', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--reviews', type=Path) + main(parser.parse_args()) diff --git a/benchmarks/agent_query/hub_role_reviews.json b/benchmarks/agent_query/hub_role_reviews.json new file mode 100644 index 000000000..f3a7f5096 --- /dev/null +++ b/benchmarks/agent_query/hub_role_reviews.json @@ -0,0 +1,1099 @@ +{ + "schema": "compass.hub-source-review/1", + "scope": "Post-output source-role census of the original fifty returned MCP hubs per tool. Manually reviewed roles describe declarations/containers, not cohesion or god-object defects; unresolved display identities stay unknown. Different returned sets prohibit a shared precision score.", + "roles": { + "type": "Production type declaration, including class, struct, alias, interface, and public testing API types.", + "callable": "Production function or method.", + "test-helper": "Callable used by repository tests.", + "test-type": "Test suite class.", + "source-module": "Whole source-file module container.", + "test-module": "Whole test-file module container.", + "example-callable": "Function in an example application.", + "benchmark-callable": "Benchmark support function.", + "generic-implementation": "Implementation block whose target is its own generic parameter.", + "trait-implementation": "Trait implementation block, not a new type declaration." + }, + "inputRunSha256": "b92037e13a0e4300e7981e71e31cb34c109cf6318c9ca33ef74c6eeb7f046889", + "sources": [ + { + "repository": "cobra", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "compassGraphSha256": "6f862024bd68b88b734a43421a21cc2074a94ab7d68cc8857d59609112c9c7db", + "graphifyGraphSha256": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + { + "repository": "flask", + "commit": "d73fa1cdcbd8b1465c151db8924ba58b1dd14e35", + "compassGraphSha256": "227fe2d1f74cf835caa3ec15897f05544832fbf7f394e1c02e5134101a07c3ad", + "graphifyGraphSha256": "ca35435af286c5c565fc66f55de569c2e3235f75c902182290ee15603ff103cc" + }, + { + "repository": "gson", + "commit": "15ca7360379cf3c1502b59981569050489f2d73e", + "compassGraphSha256": "5856ddc99ec4025c9e36d128783e6e7393f45af76f617caa38fd6938aca23ea7", + "graphifyGraphSha256": "92471ab8f5bb5d34aedddd2263fa24a43ae4ed31b34d06c8d433fe0f0dada447" + }, + { + "repository": "zod", + "commit": "d2b135cfb7a3582b9eb515756b9166bcb9521f4a", + "compassGraphSha256": "e33192de01f403cfde6c0030ba219c8e24fd26b561b984b6b710a46b3cc981e8", + "graphifyGraphSha256": "c0f6d021616326da6b7daf5105b3b996c0bb4ba17c0bf54a062f2e692147b01a" + }, + { + "repository": "axum", + "commit": "af1345b53a259b0990be1ff853f9b56c05040ef7", + "compassGraphSha256": "2cc12d5060c83389c077e4eade892991fa8f7021b59d8f6f1dc98c14c2109812", + "graphifyGraphSha256": "6ed791a771190e98ba2905e08480a9fa6ed733f46d56212ae3a4c90018d79b52" + } + ], + "reviews": [ + { + "repository": "cobra", + "tool": "compass", + "rank": 1, + "role": "type", + "id": "sha256:697caebc2e9b08da3f310e1054e66318c27899d93c59f22ceb9c3eafe56e6bf1", + "file": "command.go", + "line": 54, + "sourceText": "type Command struct {\n\t// Use is the one-line usage message.\n\t// Recommended syntax is as follows:\n\t// [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required.", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 2, + "role": "test-helper", + "id": "sha256:65d81c33651a9df51b41c907a161e2657c76cb30b22149638c54940353e420df", + "file": "command_test.go", + "line": 32, + "sourceText": "func executeCommand(root *Command, args ...string) (output string, err error) {\n\t_, output, err = executeCommandC(root, args...)\n\treturn output, err\n}", + "sourceFileSha256": "710a689a1512af3ca376a0aaaa2c0b77d10ea174f7e27db85c2f2316a087d0f3" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 3, + "role": "callable", + "id": "sha256:158d70a6b4febb5205ba5b2a13ee8f6f421942b66caa59ab479bf5b648f87819", + "file": "command.go", + "line": 1342, + "sourceText": "func (c *Command) AddCommand(cmds ...*Command) {\n\tfor i, x := range cmds {\n\t\tif cmds[i] == c {\n\t\t\tpanic(\"Command can't be a child of itself\")", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 4, + "role": "callable", + "id": "sha256:63fbc5cb0c7c405f776df1f05df085d1ab154255d84d03445818b33850cb2477", + "file": "command.go", + "line": 1688, + "sourceText": "func (c *Command) Flags() *flag.FlagSet {\n\tif c.flags == nil {\n\t\tc.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)\n\t\tif c.flagErrorBuf == nil {", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 5, + "role": "test-helper", + "id": "sha256:0dd5fd58be0e00f763db58bd16189e0f9624ced373517133c92e04876c19642b", + "file": "args_test.go", + "line": 23, + "sourceText": "func getCommand(args PositionalArgs, withValid bool) *Command {\n\tc := &Command{\n\t\tUse: \"c\",\n\t\tArgs: args,", + "sourceFileSha256": "37058f718eb5ffc7d70f917d694c04aa11fc39ba32ad8d1db9766e9d8eb009ae" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 6, + "role": "test-helper", + "id": "sha256:acfa353e8d2a1aa41d298243a03683ef41d5b8dfb2a38ff65b0b0d3b29ea6656", + "file": "command_test.go", + "line": 74, + "sourceText": "func checkStringContains(t *testing.T, got, expected string) {\n\tif !strings.Contains(got, expected) {\n\t\tt.Errorf(\"Expected to contain: \\n %v\\nGot:\\n %v\\n\", expected, got)\n\t}", + "sourceFileSha256": "710a689a1512af3ca376a0aaaa2c0b77d10ea174f7e27db85c2f2316a087d0f3" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 7, + "role": "callable", + "id": "sha256:992b92738e6eb358b0541f388d99cae179b9ad61118a3cb07ef768e10fe82084", + "file": "command.go", + "line": 1541, + "sourceText": "func (c *Command) Name() string {\n\tname := c.Use\n\ti := strings.Index(name, \" \")\n\tif i >= 0 {", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 8, + "role": "callable", + "id": "sha256:3de7fbfb848d67edf0c0628b363cd3f581648e801a6fa3145fba80f7e6ee086c", + "file": "command.go", + "line": 1775, + "sourceText": "func (c *Command) PersistentFlags() *flag.FlagSet {\n\tif c.pflags == nil {\n\t\tc.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)\n\t\tif c.flagErrorBuf == nil {", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 9, + "role": "type", + "id": "sha256:6432407b3f9310e3adc201a8d8f3fae8aac5b8e20100a2dc20ed7d60178aad93", + "file": "completions.go", + "line": 45, + "sourceText": "type ShellCompDirective int\n\ntype flagCompError struct {\n\tsubCommand string", + "sourceFileSha256": "b28a4f6c509acde7367323e835cfb4f4939fe656dd3214320d76f53e91743c89" + }, + { + "repository": "cobra", + "tool": "compass", + "rank": 10, + "role": "test-helper", + "id": "sha256:e8ccb6d6f925b6b6b7d4c2e695e22c4347403a5aab92d20cc6ae14b57e8457fd", + "file": "cobra_test.go", + "line": 27, + "sourceText": "func assertNoErr(t *testing.T, e error) {\n\tif e != nil {\n\t\tt.Error(e)\n\t}", + "sourceFileSha256": "bb9a5a989701f8df0d8797ceb0365ce3076052fcaa190aa8989b3b479bff6e09" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 1, + "role": "test-helper", + "id": "command_test_executecommand", + "file": "command_test.go", + "line": 32, + "sourceText": "func executeCommand(root *Command, args ...string) (output string, err error) {\n\t_, output, err = executeCommandC(root, args...)\n\treturn output, err\n}", + "sourceFileSha256": "710a689a1512af3ca376a0aaaa2c0b77d10ea174f7e27db85c2f2316a087d0f3" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 2, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 3, + "role": "test-helper", + "id": "args_test_getcommand", + "file": "args_test.go", + "line": 23, + "sourceText": "func getCommand(args PositionalArgs, withValid bool) *Command {\n\tc := &Command{\n\t\tUse: \"c\",\n\t\tArgs: args,", + "sourceFileSha256": "37058f718eb5ffc7d70f917d694c04aa11fc39ba32ad8d1db9766e9d8eb009ae" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 4, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 5, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 6, + "role": "callable", + "id": "cobra_writestringandcheck", + "file": "cobra.go", + "line": 243, + "sourceText": "func WriteStringAndCheck(b io.StringWriter, s string) {\n\t_, err := b.WriteString(s)\n\tCheckErr(err)\n}", + "sourceFileSha256": "aae7502e789e42469bcb2fc110af87fd02451d3b509c5b0bed782ada17338c5f" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 7, + "role": "callable", + "id": "command_defaultusagefunc", + "file": "command.go", + "line": 1974, + "sourceText": "func defaultUsageFunc(w io.Writer, in interface{}) error {\n\tc := in.(*Command)\n\tfmt.Fprint(w, \"Usage:\")\n\tif c.Runnable() {", + "sourceFileSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 8, + "role": "test-helper", + "id": "args_test_expectsuccess", + "file": "args_test.go", + "line": 35, + "sourceText": "func expectSuccess(output string, err error, t *testing.T) {\n\tif output != \"\" {\n\t\tt.Errorf(\"Unexpected output: %v\", output)\n\t}", + "sourceFileSha256": "37058f718eb5ffc7d70f917d694c04aa11fc39ba32ad8d1db9766e9d8eb009ae" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 9, + "role": "callable", + "id": "args_exactargs", + "file": "args.go", + "line": 107, + "sourceText": "func ExactArgs(n int) PositionalArgs {\n\treturn func(cmd *Command, args []string) error {\n\t\tif len(args) != n {\n\t\t\treturn fmt.Errorf(\"accepts %d arg(s), received %d\", n, len(args))", + "sourceFileSha256": "15b870d1e8a0a10341675ddee8e20bef92a21883257b6b3b11110944a573a2e7" + }, + { + "repository": "cobra", + "tool": "graphify", + "rank": 10, + "role": "test-helper", + "id": "bash_completions_test_check", + "file": "bash_completions_test.go", + "line": 33, + "sourceText": "func check(t *testing.T, found, expected string) {\n\tif !strings.Contains(found, expected) {\n\t\tt.Errorf(\"Expecting to contain: \\n %q\\nGot:\\n %q\\n\", expected, found)\n\t}", + "sourceFileSha256": "7bb8b9de6c45ba25595ca21feadea339be1851734ad26c750d3beaf715451e20" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 1, + "role": "type", + "id": "sha256:b9fa348bcd3398661d65066e09c6d664db2f69dabf5c8bb7c9f2db7e7d91f837", + "file": "src/flask/app.py", + "line": 110, + "sourceText": "class Flask(App):\n \"\"\"The flask object implements a WSGI application and acts as the central\n object. It is passed the name of the module or package of the\n application. Once it is created it will act as a central registry for", + "sourceFileSha256": "975de70f74626460f7afd2f16e3a513cbc2fe65cbd961f95c2f92eec4f7ecff8" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 2, + "role": "type", + "id": "sha256:de408b7ede5843f7c05d6218976c13afb68070dd25dfb892ec92a058e3f49903", + "file": "src/flask/sansio/app.py", + "line": 59, + "sourceText": "class App(Scaffold):\n \"\"\"The flask object implements a WSGI application and acts as the central\n object. It is passed the name of the module or package of the\n application. Once it is created it will act as a central registry for", + "sourceFileSha256": "410e21f53b5da3f5b73918733160dccfc22c17b0b48fe101dea56bad001c03f3" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 3, + "role": "type", + "id": "sha256:ddea8a25218008cb45a975a1c215a0f3c3a35afa7f05e66b3b3a34cbebb68040", + "file": "src/flask/ctx.py", + "line": 260, + "sourceText": "class AppContext:\n \"\"\"An app context contains information about an app, and about the request\n when handling a request. A context is pushed at the beginning of each\n request and CLI command, and popped at the end. The context is referred to", + "sourceFileSha256": "e21952d044159438765eb27848b0fe4c75f83faee8645181a09a1eccb1d5cab7" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 4, + "role": "callable", + "id": "sha256:25951e78e81c124582497db358d2bc38dccac1b4a26e9bd11d3f0202ed1b80fc", + "file": "src/flask/sansio/scaffold.py", + "line": 42, + "sourceText": "def setupmethod(f: F) -> F:\n f_name = f.__name__\n\n def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any:", + "sourceFileSha256": "d6578d2b5f06d227482e4110f8b750d04c8d94c9525c8857f9183f727ee2858b" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 5, + "role": "type", + "id": "sha256:8dab899b8b812824c068b665ff78e63daa313a7c3a7365a98cfcbd5c295aba01", + "file": "src/flask/wrappers.py", + "line": 18, + "sourceText": "class Request(RequestBase):\n \"\"\"The request object used by default in Flask. Remembers the\n matched endpoint and view arguments.\n", + "sourceFileSha256": "8d492fe2655e93622ae21c3177846faab20c6faf41bf43c49438162e6779391a" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 6, + "role": "type", + "id": "sha256:b4696651e154855b4bce7a5e85d74be3985bcf1f7cf3bfd0ee8ee47eb6bb568f", + "file": "src/flask/sansio/scaffold.py", + "line": 52, + "sourceText": "class Scaffold:\n \"\"\"Common behavior shared between :class:`~flask.Flask` and\n :class:`~flask.blueprints.Blueprint`.\n", + "sourceFileSha256": "d6578d2b5f06d227482e4110f8b750d04c8d94c9525c8857f9183f727ee2858b" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 7, + "role": "type", + "id": "sha256:2ab354fa1c81ea0fdfcbb560699e389ab06daa6fc67ac4dfdcb7cf9cc3f230ca", + "file": "src/flask/sansio/blueprints.py", + "line": 119, + "sourceText": "class Blueprint(Scaffold):\n \"\"\"Represents a blueprint, a collection of routes and other\n app-related functions that can be registered on a real application\n later.", + "sourceFileSha256": "da960c5792daf2f1cef8c14113d9c95c8ce389a9689abb395fe867f4054d0ba1" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 8, + "role": "type", + "id": "sha256:41a3a9d39eb3e0c632b3e7e75c872902d3e08e27e2162027f88668fe839ef569", + "file": "src/flask/wrappers.py", + "line": 222, + "sourceText": "class Response(ResponseBase):\n \"\"\"The response object that is used by default in Flask. Works like the\n response object from Werkzeug but is set to have an HTML mimetype by\n default. Quite often you don't have to create this object yourself because", + "sourceFileSha256": "8d492fe2655e93622ae21c3177846faab20c6faf41bf43c49438162e6779391a" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 9, + "role": "example-callable", + "id": "sha256:4833138a290c37135cf24616ad4c7b33cbf3027d1ecb2559d63562afad079006", + "file": "examples/tutorial/flaskr/db.py", + "line": 9, + "sourceText": "def get_db():\n \"\"\"Connect to the application's configured database. The connection\n is unique for each request and will be reused if this is called\n again.", + "sourceFileSha256": "700f9d0a455bf79c9bf6de4f2784f13b96256faea19c15ddee71fb7957e14c31" + }, + { + "repository": "flask", + "tool": "compass", + "rank": 10, + "role": "type", + "id": "sha256:9eddf1514cd1c841f0010176c064ce5ab91686f8b66a0adf8946e553606c6d8d", + "file": "src/flask/sessions.py", + "line": 24, + "sourceText": "class SessionMixin(MutableMapping[str, t.Any]):\n \"\"\"Expands a basic dictionary with session attributes.\"\"\"\n\n @property", + "sourceFileSha256": "7b2c11aa6cad4e66275ff102efcf980462684dce570ff951a61406e4b6cdd5dd" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 1, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 2, + "role": "type", + "id": "src_flask_sansio_app_app", + "file": "src/flask/sansio/app.py", + "line": 59, + "sourceText": "class App(Scaffold):\n \"\"\"The flask object implements a WSGI application and acts as the central\n object. It is passed the name of the module or package of the\n application. Once it is created it will act as a central registry for", + "sourceFileSha256": "410e21f53b5da3f5b73918733160dccfc22c17b0b48fe101dea56bad001c03f3" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 3, + "role": "callable", + "id": "src_flask_sansio_scaffold_setupmethod", + "file": "src/flask/sansio/scaffold.py", + "line": 42, + "sourceText": "def setupmethod(f: F) -> F:\n f_name = f.__name__\n\n def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any:", + "sourceFileSha256": "d6578d2b5f06d227482e4110f8b750d04c8d94c9525c8857f9183f727ee2858b" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 4, + "role": "type", + "id": "src_flask_ctx_appcontext", + "file": "src/flask/ctx.py", + "line": 260, + "sourceText": "class AppContext:\n \"\"\"An app context contains information about an app, and about the request\n when handling a request. A context is pushed at the beginning of each\n request and CLI command, and popped at the end. The context is referred to", + "sourceFileSha256": "e21952d044159438765eb27848b0fe4c75f83faee8645181a09a1eccb1d5cab7" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 5, + "role": "type", + "id": "src_flask_sansio_scaffold_scaffold", + "file": "src/flask/sansio/scaffold.py", + "line": 52, + "sourceText": "class Scaffold:\n \"\"\"Common behavior shared between :class:`~flask.Flask` and\n :class:`~flask.blueprints.Blueprint`.\n", + "sourceFileSha256": "d6578d2b5f06d227482e4110f8b750d04c8d94c9525c8857f9183f727ee2858b" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 6, + "role": "type", + "id": "src_flask_wrappers_response", + "file": "src/flask/wrappers.py", + "line": 222, + "sourceText": "class Response(ResponseBase):\n \"\"\"The response object that is used by default in Flask. Works like the\n response object from Werkzeug but is set to have an HTML mimetype by\n default. Quite often you don't have to create this object yourself because", + "sourceFileSha256": "8d492fe2655e93622ae21c3177846faab20c6faf41bf43c49438162e6779391a" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 7, + "role": "type", + "id": "src_flask_wrappers_request", + "file": "src/flask/wrappers.py", + "line": 18, + "sourceText": "class Request(RequestBase):\n \"\"\"The request object used by default in Flask. Remembers the\n matched endpoint and view arguments.\n", + "sourceFileSha256": "8d492fe2655e93622ae21c3177846faab20c6faf41bf43c49438162e6779391a" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 8, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 9, + "role": "type", + "id": "src_flask_testing_flaskclient", + "file": "src/flask/testing.py", + "line": 109, + "sourceText": "class FlaskClient(Client):\n \"\"\"Works like a regular Werkzeug test client, with additional behavior for\n Flask. Can defer the cleanup of the request context until the end of a\n ``with`` block. For general information about how to use this class refer to", + "sourceFileSha256": "629d481658aaec7076a55913eda38c270ec45d430b34b2f9d38b2eec2b0ad3da" + }, + { + "repository": "flask", + "tool": "graphify", + "rank": 10, + "role": "example-callable", + "id": "examples_tutorial_flaskr_db_get_db", + "file": "examples/tutorial/flaskr/db.py", + "line": 9, + "sourceText": "def get_db():\n \"\"\"Connect to the application's configured database. The connection\n is unique for each request and will be reused if this is called\n again.", + "sourceFileSha256": "700f9d0a455bf79c9bf6de4f2784f13b96256faea19c15ddee71fb7957e14c31" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 1, + "role": "type", + "id": "sha256:537e981221a007ba87ff810461ae985b77fe37f06c53093ab92814e0c635f262", + "file": "gson/src/main/java/com/google/gson/stream/JsonReader.java", + "line": 211, + "sourceText": "public class JsonReader implements Closeable {\n private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;\n\n private static final int PEEKED_NONE = 0;", + "sourceFileSha256": "46321d93477d0ca372aea8d8f62431ea44b7ca8947afea2f474bdebc2ff1e589" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 2, + "role": "type", + "id": "sha256:8747e09a8e903568e8a964201f3cc56893d5a0943cb2d4d51d2692ae357c0645", + "file": "gson/src/main/java/com/google/gson/GsonBuilder.java", + "line": 93, + "sourceText": "public final class GsonBuilder {\n private static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;\n // Strictness of `null` is the legacy mode where some Gson APIs are always lenient\n private static final Strictness DEFAULT_STRICTNESS = null;", + "sourceFileSha256": "31a5577eee8ab35fa2fab617741d5c02067b7cab6f7db4a64c78544a05ea1d13" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 3, + "role": "type", + "id": "sha256:4fd7c5d5c89427e0b28b94c6c2de683a6533fd6ea6170d6e9c567fe4da76ac56", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 156, + "sourceText": "public final class Gson {\n\n private static final String JSON_NON_EXECUTABLE_PREFIX = \")]}'\\n\";\n", + "sourceFileSha256": "1a33f3eb5ddc01f0a33bbe2b43dc26f8474fc8d5b80876ad9a2f33056224fc94" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 4, + "role": "callable", + "id": "sha256:9bdfcab0686beea84640af610c6c6889a412345b01c262629f143664459c4897", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 850, + "sourceText": " public T fromJson(String json, Class classOfT) throws JsonSyntaxException {\n return fromJson(json, TypeToken.get(classOfT));\n }\n", + "sourceFileSha256": "1a33f3eb5ddc01f0a33bbe2b43dc26f8474fc8d5b80876ad9a2f33056224fc94" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 5, + "role": "type", + "id": "sha256:62389b55bcef34c86223c0c60d5f6840b5b5dc284c00baadcf2f77583be99484", + "file": "gson/src/main/java/com/google/gson/reflect/TypeToken.java", + "line": 54, + "sourceText": "public class TypeToken {\n private final Class rawType;\n private final Type type;\n private final int hashCode;", + "sourceFileSha256": "f182b1fe6d242196dd846617bf594ce45741103d17bbc3f148219bfeb23db8bd" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 6, + "role": "type", + "id": "sha256:2222e85b9b69a13e38de1c9654b1ce8a86e75815310c570ee793038eab5017b8", + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "sourceText": "public class JsonWriter implements Closeable, Flushable {\n\n // Syntax as defined by https://datatracker.ietf.org/doc/html/rfc8259#section-6\n private static final Pattern VALID_JSON_NUMBER_PATTERN =", + "sourceFileSha256": "6b739b0ba2cc5a8e6809f1bdd31244c12e4f9a7145f6e93cd8f39ea193bf0e5b" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 7, + "role": "type", + "id": "sha256:f89ff78bd1ce026094d6afc709732ceda3547d33e541b4e12b9314a967e70894", + "file": "gson/src/main/java/com/google/gson/TypeAdapter.java", + "line": 122, + "sourceText": "public abstract class TypeAdapter {\n\n public TypeAdapter() {}\n", + "sourceFileSha256": "3b13e9c2a9337369e61fa32c493a6fbb80b011b0876c779c2f95ab1464c9e57c" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 8, + "role": "type", + "id": "sha256:03843efb2824f56acf2de39a4ad7a5467887e8c106f526a1a38837407c8dd1d2", + "file": "gson/src/main/java/com/google/gson/JsonPrimitive.java", + "line": 35, + "sourceText": "public final class JsonPrimitive extends JsonElement {\n\n private final Object value;\n", + "sourceFileSha256": "5b466c1eee42aa86fd7106469858f121d1a062b1c6c4b48269afd96e78c051e4" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 9, + "role": "callable", + "id": "sha256:e64ab7001f5ee8b0275e8c1b857280a9c621d38902a4ae0ac9754ed0f27aa075", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 565, + "sourceText": " public String toJson(Object src) {\n if (src == null) {\n return toJson(JsonNull.INSTANCE);\n }", + "sourceFileSha256": "1a33f3eb5ddc01f0a33bbe2b43dc26f8474fc8d5b80876ad9a2f33056224fc94" + }, + { + "repository": "gson", + "tool": "compass", + "rank": 10, + "role": "type", + "id": "sha256:da1b62e1a2d2be089320afcf1143b2e9e5915a28cfeda233929ca6c962c6797b", + "file": "gson/src/main/java/com/google/gson/JsonElement.java", + "line": 96, + "sourceText": "public abstract class JsonElement {\n /**\n * @deprecated Creating custom {@code JsonElement} subclasses is highly discouraged and can lead\n * to undefined behavior.
", + "sourceFileSha256": "3215d81cdf2b213c7250cbb7703c793595819b9bac4ffd246034d4eb9fdf5e19" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 1, + "role": "type", + "id": "gson_src_main_java_com_google_gson_reflect_typetoken_typetoken", + "file": "gson/src/main/java/com/google/gson/reflect/TypeToken.java", + "line": 54, + "sourceText": "public class TypeToken {\n private final Class rawType;\n private final Type type;\n private final int hashCode;", + "sourceFileSha256": "f182b1fe6d242196dd846617bf594ce45741103d17bbc3f148219bfeb23db8bd" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 2, + "role": "type", + "id": "gson_src_main_java_com_google_gson_gson_gson", + "file": "gson/src/main/java/com/google/gson/Gson.java", + "line": 156, + "sourceText": "public final class Gson {\n\n private static final String JSON_NON_EXECUTABLE_PREFIX = \")]}'\\n\";\n", + "sourceFileSha256": "1a33f3eb5ddc01f0a33bbe2b43dc26f8474fc8d5b80876ad9a2f33056224fc94" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 3, + "role": "type", + "id": "gson_src_main_java_com_google_gson_stream_jsonreader_jsonreader", + "file": "gson/src/main/java/com/google/gson/stream/JsonReader.java", + "line": 211, + "sourceText": "public class JsonReader implements Closeable {\n private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;\n\n private static final int PEEKED_NONE = 0;", + "sourceFileSha256": "46321d93477d0ca372aea8d8f62431ea44b7ca8947afea2f474bdebc2ff1e589" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 4, + "role": "type", + "id": "gson_src_main_java_com_google_gson_jsonelement_jsonelement", + "file": "gson/src/main/java/com/google/gson/JsonElement.java", + "line": 96, + "sourceText": "public abstract class JsonElement {\n /**\n * @deprecated Creating custom {@code JsonElement} subclasses is highly discouraged and can lead\n * to undefined behavior.
", + "sourceFileSha256": "3215d81cdf2b213c7250cbb7703c793595819b9bac4ffd246034d4eb9fdf5e19" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 5, + "role": "type", + "id": "gson_src_main_java_com_google_gson_typeadapter_typeadapter", + "file": "gson/src/main/java/com/google/gson/TypeAdapter.java", + "line": 122, + "sourceText": "public abstract class TypeAdapter {\n\n public TypeAdapter() {}\n", + "sourceFileSha256": "3b13e9c2a9337369e61fa32c493a6fbb80b011b0876c779c2f95ab1464c9e57c" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 6, + "role": "type", + "id": "gson_src_main_java_com_google_gson_stream_jsonwriter_jsonwriter", + "file": "gson/src/main/java/com/google/gson/stream/JsonWriter.java", + "line": 162, + "sourceText": "public class JsonWriter implements Closeable, Flushable {\n\n // Syntax as defined by https://datatracker.ietf.org/doc/html/rfc8259#section-6\n private static final Pattern VALID_JSON_NUMBER_PATTERN =", + "sourceFileSha256": "6b739b0ba2cc5a8e6809f1bdd31244c12e4f9a7145f6e93cd8f39ea193bf0e5b" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 7, + "role": "test-type", + "id": "gson_src_test_java_com_google_gson_stream_jsonreadertest_jsonreadertest", + "file": "gson/src/test/java/com/google/gson/stream/JsonReaderTest.java", + "line": 40, + "sourceText": "@SuppressWarnings(\"resource\")\npublic final class JsonReaderTest {\n\n @Test", + "sourceFileSha256": "7fb35a955e2f46fc9aed9176a3f3301dc59d198419d57e22f300d14f615f94c4" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 8, + "role": "type", + "id": "gson_src_main_java_com_google_gson_gsonbuilder_gsonbuilder", + "file": "gson/src/main/java/com/google/gson/GsonBuilder.java", + "line": 93, + "sourceText": "public final class GsonBuilder {\n private static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;\n // Strictness of `null` is the legacy mode where some Gson APIs are always lenient\n private static final Strictness DEFAULT_STRICTNESS = null;", + "sourceFileSha256": "31a5577eee8ab35fa2fab617741d5c02067b7cab6f7db4a64c78544a05ea1d13" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 9, + "role": "test-type", + "id": "gson_src_test_java_com_google_gson_functional_primitivetest_primitivetest", + "file": "gson/src/test/java/com/google/gson/functional/PrimitiveTest.java", + "line": 44, + "sourceText": "public class PrimitiveTest {\n private Gson gson;\n\n @Before", + "sourceFileSha256": "5cc2e522e5babdceef8192e6cb5cca13cae1c26eb63da0ed53a0ea80bb7bbe7b" + }, + { + "repository": "gson", + "tool": "graphify", + "rank": 10, + "role": "test-type", + "id": "gson_src_test_java_com_google_gson_functional_defaulttypeadapterstest_defaulttypeadapterstest", + "file": "gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java", + "line": 89, + "sourceText": "@SuppressWarnings(\"JavaUtilDate\")\npublic class DefaultTypeAdaptersTest {\n private Gson gson;\n private TimeZone oldTimeZone;", + "sourceFileSha256": "7cd95d5cd7271ec27c264f36cc017f8980feb346efce5a6caa430b2d9a706952" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 1, + "role": "source-module", + "id": "sha256:f738b0c1f5d24863d64fab8475d14c9450aa31631743cbe083f335844f05c195", + "file": "packages/zod/src/v4/core/schemas.ts", + "line": 1, + "sourceText": "import * as checks from \"./checks.js\";\nimport type { $ZodNumberFormats } from \"./checks.js\";\nimport type { $ZodBigIntFormats } from \"./checks.js\";\nimport * as core from \"./core.js\";", + "sourceFileSha256": "12372f51ba3e437cca064a5c743bea11bb49fc1616e3dc084dac83cc67a91f72" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 2, + "role": "source-module", + "id": "sha256:4839121225cd7b794e6b3003ae6353a387711a2ec314fae03fb27d5119abe6b0", + "file": "packages/zod/src/v4/classic/schemas.ts", + "line": 1, + "sourceText": "import type { $ZodBigIntFormats } from \"../core/checks.js\";\nimport * as core from \"../core/index.js\";\nimport { util, type $ZodNumberFormats } from \"../core/index.js\";\nimport * as processors from \"../core/json-schema-processors.js\";", + "sourceFileSha256": "c79403c4f5da6b480779d9ec65601f3fd883e99397322203f0c9b1d00ea106d4" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 3, + "role": "test-module", + "id": "sha256:f0d18b0eb1634122c407fca91d24171580b6dd9bf5b6a1be3ed08b6b57b47fa0", + "file": "packages/zod/src/v4/classic/tests/to-json-schema.test.ts", + "line": 1, + "sourceText": "import { Validator } from \"@seriousme/openapi-schema-validator\";\nimport { describe, expect, test, vi } from \"vitest\";\nimport * as z from \"zod\";\n// import * as zCore from \"zod/v4/core\";", + "sourceFileSha256": "a26ba5e1f1e8be31fdd65b1d2ca6e481832faa2f16ea58f231d616dc8eb4120a" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 4, + "role": "source-module", + "id": "sha256:c68b9b53580cb6a30f1f21ef0a236d04446328a1ef59a5242163bf6653fced15", + "file": "packages/zod/src/v4/mini/schemas.ts", + "line": 1, + "sourceText": "import * as core from \"../core/index.js\";\nimport type { $ZodBigIntFormats } from \"../core/index.js\";\nimport * as regexes from \"../core/regexes.js\";\nimport * as util from \"../core/util.js\";", + "sourceFileSha256": "0564e616637aa02917854cb0ee22bbc64172c78a111cb422ba1f577d556432ee" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 5, + "role": "test-module", + "id": "sha256:3fd0d3d2d6c99eff1c0f1d98e063e987dd6d4bab820f8b1d5fda0894bfb18eb0", + "file": "packages/zod/src/v4/core/tests/compile.test.ts", + "line": 1, + "sourceText": "import { expect, expectTypeOf, test } from \"vitest\";\n\nimport * as z from \"../../index.js\";\nimport {", + "sourceFileSha256": "82a20ec964e4a1c40514055e5113e52a29ac02aa75dbb8398cd8cf390cfdd5ea" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 6, + "role": "test-module", + "id": "sha256:9e42231af1aec41da50582f84615e89e93bc93aebbd72800c33f46cf00469f1d", + "file": "packages/zod/src/v4/classic/tests/from-json-schema.test.ts", + "line": 1, + "sourceText": "import { expect, test } from \"vitest\";\nimport { fromJSONSchema } from \"../from-json-schema.js\";\nimport * as z from \"../index.js\";\n", + "sourceFileSha256": "5ea2b7bf02920a7bb8709f1d2db0bb4d304e40bbc4927b2d6ca40f488c79ec31" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 7, + "role": "source-module", + "id": "sha256:4924f2b319a8a0fc43829d7d85e3eb5240b79b441beeaf23a23c8ff1712fa2bd", + "file": "packages/zod/src/v4/core/api.ts", + "line": 1, + "sourceText": "import * as checks from \"./checks.js\";\nimport type * as core from \"./core.js\";\nimport type * as errors from \"./errors.js\";\nimport * as registries from \"./registries.js\";", + "sourceFileSha256": "9ad4da853ced907f2e5764e5a390f087c0bc8633638a993c50699f0827a5bf5d" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 8, + "role": "source-module", + "id": "sha256:cf3531eed1cc580ec1bcff5aff8ce38c3b3442878d76b09b0e64e855870aaa79", + "file": "packages/zod/src/v3/types.ts", + "line": 1, + "sourceText": "import {\n type IssueData,\n type StringValidation,\n type ZodCustomIssue,", + "sourceFileSha256": "73b9529564a34fc028a5a62c17e09148747b737039b78b124e84254cb886411a" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 9, + "role": "test-module", + "id": "sha256:e6e4c6559c1e502bcf9b32525880968d86c52d1461aee09cc3777f9620c58300", + "file": "packages/zod/src/v4/classic/tests/cyclic-data.test.ts", + "line": 1, + "sourceText": "import v8 from \"node:v8\";\nimport vm from \"node:vm\";\nimport { expect, test } from \"vitest\";\nimport * as z from \"zod/v4\";", + "sourceFileSha256": "8dc2d5bc6bb9d3a0ad40491eaffa903e837a3fe92666c067c4d428b09583786a" + }, + { + "repository": "zod", + "tool": "compass", + "rank": 10, + "role": "source-module", + "id": "sha256:d430dadbdc4d952986b7cb934947a122a86298bf0be5a798b3117d105fede928", + "file": "packages/zod/src/v4/core/checks.ts", + "line": 1, + "sourceText": "// import { $ZodType } from \"./schemas.js\";\n\nimport * as core from \"./core.js\";\nimport type * as errors from \"./errors.js\";", + "sourceFileSha256": "d813140472a55ab36e845f4eaadbb3deae6d0597f90bef6c3f51366ac8dbfd4c" + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 1, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 2, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 3, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 4, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 5, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 6, + "role": "type", + "id": "packages_zod_src_v4_core_checks_zodstringformats", + "file": "packages/zod/src/v4/core/checks.ts", + "line": 689, + "sourceText": "export type $ZodStringFormats =\n | \"email\"\n | \"url\"\n | \"emoji\"", + "sourceFileSha256": "d813140472a55ab36e845f4eaadbb3deae6d0597f90bef6c3f51366ac8dbfd4c" + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 7, + "role": "benchmark-callable", + "id": "packages_bench_metabench_metabench", + "file": "packages/bench/metabench.ts", + "line": 14, + "sourceText": "export function metabench(name: string, benchmarks?: Benchmarks): Metabench {\n let bench: Metabench;\n if (BENCH === \"tinybench\") {\n bench = new Tinybench(name, benchmarks || {});", + "sourceFileSha256": "0db1b192313b80aa3b8502f912542a0bcf8ec919f035ca6bfc54ceb29db406af" + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 8, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 9, + "role": "type", + "id": "packages_zod_src_v4_core_schemas_zodtypeinternals", + "file": "packages/zod/src/v4/core/schemas.ts", + "line": 181, + "sourceText": "export interface $ZodTypeInternals extends _$ZodTypeInternals {\n /** @internal The inferred output type */\n output: O; //extends { $out: infer O } ? O : Out;\n /** @internal The inferred input type */", + "sourceFileSha256": "12372f51ba3e437cca064a5c743bea11bb49fc1616e3dc084dac83cc67a91f72" + }, + { + "repository": "zod", + "tool": "graphify", + "rank": 10, + "role": "type", + "id": "packages_zod_src_v3_helpers_parseutil_parseinput", + "file": "packages/zod/src/v3/helpers/parseUtil.ts", + "line": 66, + "sourceText": "export type ParseInput = {\n data: any;\n path: (string | number)[];\n parent: ParseContext;", + "sourceFileSha256": "cb94690c02dce392b98ca364de0d4b24f42db715fbb1759a08bcfb7fa6674645" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 1, + "role": "callable", + "id": "sha256:60aab06ff57a8c85fab4715992128370838e5d03eed5a9e58d82bb510287ac28", + "file": "src/routing/mod.rs", + "line": 192, + "sourceText": " pub fn route(self, path: &str, method_router: MethodRouter) -> Self {\n tap_inner!(self, mut this => {\n panic_on_err!(this.path_router.route(path, method_router));\n })", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 2, + "role": "test-helper", + "id": "sha256:68b2857794e763372a7a332a2565d51e0307a3e72c89b04f9df4a18fa960bb9a", + "file": "src/test_helpers/test_client.rs", + "line": 36, + "sourceText": " pub fn new(svc: S) -> Self\n where\n S: Service + Clone + Send + 'static,\n S::Future: Send,", + "sourceFileSha256": "08539b094aa868bb9be422a5dae0437a29dbf30a036df6431efed99ce2a582b6" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 3, + "role": "type", + "id": "sha256:45fdb21dd68855366f06d2399063b23ebd4754d6bd0fd99e8802b6833e18af6d", + "file": "src/routing/mod.rs", + "line": 86, + "sourceText": "pub struct Router {\n inner: Arc>,\n}\n", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 4, + "role": "type", + "id": "sha256:04b0b1fb883d743b22d88f96cacd55215dbcb66091ef10bff4b784aa2cbb7e1d", + "file": "src/routing/method_routing.rs", + "line": 549, + "sourceText": "pub struct MethodRouter {\n get: MethodEndpoint,\n head: MethodEndpoint,\n delete: MethodEndpoint,", + "sourceFileSha256": "d91b8dc73b3a584c57055a6fcf2aa88241c0ee7875663e6c103844741c4c9119" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 5, + "role": "callable", + "id": "sha256:ba87bdef07c319f041356e21239bc5984549ccefecd247c4ab6cf2ab00d1299b", + "file": "src/routing/mod.rs", + "line": 162, + "sourceText": " pub fn new() -> Self {\n Self {\n inner: Arc::new(RouterInner {\n path_router: Default::default(),", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 6, + "role": "callable", + "id": "sha256:a0f57e92389119330fd13358ce5f0589d5360eac2d34869176cf7a6b6813526b", + "file": "src/routing/mod.rs", + "line": 220, + "sourceText": " pub fn nest(self, path: &str, router: Self) -> Self {\n if path.is_empty() || path == \"/\" {\n panic!(\"Nesting at the root is no longer supported. Use merge instead.\");\n }", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 7, + "role": "type", + "id": "sha256:831ff61676cca7793933839f665e492e2451afbe00ab9ba8b0375e2fc4115d99", + "file": "src/routing/route.rs", + "line": 31, + "sourceText": "pub struct Route(BoxCloneSyncService);\n\nimpl Route {\n pub(crate) fn new(svc: T) -> Self", + "sourceFileSha256": "4ce2ea4d6623d1a29e156d83f93bcb9f69aa969471f0a8083a19bb320964541d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 8, + "role": "test-helper", + "id": "sha256:90ddb5e4cc5b746f56570b98d4b7c32f333a8cc8dd9ea8c740669432186a4601", + "file": "src/test_helpers/test_client.rs", + "line": 51, + "sourceText": " pub fn get(&self, url: &str) -> RequestBuilder {\n RequestBuilder {\n builder: self.client.get(format!(\"http://{}{url}\", self.addr)),\n }", + "sourceFileSha256": "08539b094aa868bb9be422a5dae0437a29dbf30a036df6431efed99ce2a582b6" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 9, + "role": "callable", + "id": "sha256:d42797d051fe003ed59e07df110a2c316fdfe6d7be5741344cb3946dd798321f", + "file": "src/routing/mod.rs", + "line": 336, + "sourceText": " pub fn fallback(self, handler: H) -> Self\n where\n H: Handler,\n T: 'static,", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "compass", + "rank": 10, + "role": "callable", + "id": "sha256:dc9b52391b0c9fcd4b93234ce6294eb8f8cc7d85f3f317f2c49f2703c5fa7073", + "file": "src/routing/mod.rs", + "line": 296, + "sourceText": " pub fn layer(self, layer: L) -> Self\n where\n L: Layer + Clone + Send + Sync + 'static,\n L::Service: Service + Clone + Send + Sync + 'static,", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 1, + "role": "generic-implementation", + "id": "src_service_ext_s", + "file": "src/service_ext.rs", + "line": 47, + "sourceText": "impl ServiceExt for S\nwhere\n S: Service + Sized,\n{", + "sourceFileSha256": "e212b899108791b586d442e9c0a294ef908cfa97652b68c8770c782d1efd07cb" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 2, + "role": "generic-implementation", + "id": "src_handler_mod_t", + "file": "src/handler/mod.rs", + "line": 272, + "sourceText": "impl Handler for T\nwhere\n T: IntoResponse + Clone + Send + Sync + 'static,\n{", + "sourceFileSha256": "db398d9a153d8e9fbff07fc328d3805713171c7f378938edb78c426483c701ba" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 3, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 4, + "role": "test-helper", + "id": "src_routing_method_routing_call", + "file": "src/routing/method_routing.rs", + "line": 1739, + "sourceText": " async fn call(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)\n where\n S: Service,\n S::Response: IntoResponse,", + "sourceFileSha256": "d91b8dc73b3a584c57055a6fcf2aa88241c0ee7875663e6c103844741c4c9119" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 5, + "role": "trait-implementation", + "id": "src_routing_mod_router_s", + "file": "src/routing/mod.rs", + "line": 90, + "sourceText": "impl Clone for Router {\n fn clone(&self) -> Self {\n Self {\n inner: Arc::clone(&self.inner),", + "sourceFileSha256": "7ecd918b9285812f0edfad5cc48d9ad6268c2dae9cc497089fbec6cc2eb08b7d" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 6, + "role": null, + "reason": "Returned label does not uniquely identify a stored node; no oracle ID substitution." + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 7, + "role": "type", + "id": "src_routing_route_route", + "file": "src/routing/route.rs", + "line": 31, + "sourceText": "pub struct Route(BoxCloneSyncService);\n\nimpl Route {\n pub(crate) fn new(svc: T) -> Self", + "sourceFileSha256": "4ce2ea4d6623d1a29e156d83f93bcb9f69aa969471f0a8083a19bb320964541d" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 8, + "role": "type", + "id": "src_routing_method_filter_methodfilter", + "file": "src/routing/method_filter.rs", + "line": 9, + "sourceText": "pub struct MethodFilter(u16);\n\nimpl MethodFilter {\n /// Match `CONNECT` requests.", + "sourceFileSha256": "d1c354426afb62a22f4cd518b676e2c5e0f419f7c056c59380e4aa51dd2469b1" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 9, + "role": "trait-implementation", + "id": "src_extract_ws_bytes", + "file": "src/extract/ws.rs", + "line": 769, + "sourceText": "impl From for Bytes {\n #[inline]\n fn from(Utf8Bytes(bytes): Utf8Bytes) -> Self {\n bytes.into()", + "sourceFileSha256": "88dd9b63ef3ea7d4ed2d37bc83a0278898c48255bcd9032f386bcc7c7f376a04" + }, + { + "repository": "axum", + "tool": "graphify", + "rank": 10, + "role": "type", + "id": "src_extract_ws_utf8bytes", + "file": "src/extract/ws.rs", + "line": 681, + "sourceText": "pub struct Utf8Bytes(ts::Utf8Bytes);\n\nimpl Utf8Bytes {\n /// Creates from a static str.", + "sourceFileSha256": "88dd9b63ef3ea7d4ed2d37bc83a0278898c48255bcd9032f386bcc7c7f376a04" + } + ] +} diff --git a/benchmarks/agent_query/tests/test_hub_evidence.py b/benchmarks/agent_query/tests/test_hub_evidence.py new file mode 100644 index 000000000..1dfdb168c --- /dev/null +++ b/benchmarks/agent_query/tests/test_hub_evidence.py @@ -0,0 +1,86 @@ +import copy +import hashlib +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.hub_evidence_audit import connectivity, check_summary, check_review, check_text_rows, check_request + + +class HubEvidenceTests(unittest.TestCase): + def test_requests_cannot_be_relabelled_after_capture(self): + row=dict(response=dict(id=5),arguments=dict(top_n=10)) + request=dict(jsonrpc='2.0',method='tools/call',id=5,params=dict(name='god_nodes',arguments=dict(top_n=10))) + check_request(request,row) + for changed in [dict(top_n=1),dict(top_n=True),dict(top_n=10,extra='bad')]: + request['params']['arguments']=changed + with self.assertRaises(ValueError):check_request(request,row) + + def graph(self, directed=True): + return {'directed': directed, 'nodes':[{'id':'a'}, {'id':'b'}], 'links':[ + {'source':'a','target':'b','kind':'calls'}, + {'source':'a','target':'b','kind':'calls'}, + {'source':'b','target':'a','kind':'references'}, + {'source':'a','target':'a','kind':'calls'}, + {'source':'a','target':'missing','kind':'calls'}]} + + def test_parallel_and_loop_records_are_not_pair_degree(self): + result=connectivity(self.graph(),'compass','a') + self.assertEqual(result['edgeRecords'],4) + self.assertEqual(result['selfLoopRecords'],1) + self.assertEqual(result['relations'][0],dict(relation='calls',edgeRecords=3, + incomingRecords=1,outgoingRecords=3,undirectedRecords=0)) + + def test_undirected_does_not_invent_arrow_directions(self): + graph=self.graph(False) + for edge in graph['links']: + edge['relation']=edge.pop('kind') + result=connectivity(graph,'graphify','a') + self.assertEqual(result['relations'][0],dict(relation='calls',edgeRecords=3, + incomingRecords=0,outgoingRecords=0,undirectedRecords=3)) + + def test_cap_reports_omitted_records_and_stable_order(self): + graph=self.graph() + graph['links']=[dict(source='a',target='b',kind=f'r{i:02}') for i in range(20)] + graph['links'].append(dict(source='a',target='b',kind='r19')) + result=connectivity(graph,'compass','a') + self.assertEqual(result['edgeRecords'],21) + self.assertEqual(result['omittedRelationKinds'],4) + self.assertEqual(result['omittedRelationRecords'],4) + self.assertEqual([r['relation'] for r in result['relations'][:2]],['r19','r00']) + graph['links'].reverse() + self.assertEqual(result,connectivity(graph,'compass','a')) + + def test_bad_count_direction_and_boolean_counter_fail(self): + expected=connectivity(self.graph(),'compass','a') + self.assertTrue(check_summary(expected,expected)) + for key,value in [('edgeRecords',5),('selfLoopRecords',True),('directed',False)]: + actual=copy.deepcopy(expected);actual[key]=value + self.assertFalse(check_summary(actual,expected)) + actual=copy.deepcopy(expected);actual['relations'][0]['incomingRecords']=0 + self.assertFalse(check_summary(actual,expected)) + + def test_text_rows_must_belong_to_the_right_hub(self): + graph=self.graph();graph['links']=graph['links'][:1] + expected=connectivity(graph,'compass','a') + block='\n kind: function | incident records: 1 | self-loops: 0\n relation "calls": incoming 0, outgoing 1, undirected 0' + text=' 1. A - 1 edges'+block+'\n 2. B - 1 edges' + self.assertTrue(check_text_rows(text,1,expected)) + self.assertFalse(check_text_rows(text,2,expected)) + self.assertFalse(check_text_rows(text.replace('outgoing 1','outgoing 2'),1,expected)) + + def test_review_requires_exact_source_and_contains_paths(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory);data=b'class Service:\n pass\n';(root/'a.py').write_bytes(data) + node={'id':'a','name':'Service','source':{'file':'a.py','startLine':1}} + review=dict(id='a',file='a.py',line=1,sourceText=data.decode().rstrip('\n'), + sourceFileSha256=hashlib.sha256(data).hexdigest()) + check_review(review,node,'compass',root) + review['sourceText']='wrong' + with self.assertRaises(ValueError):check_review(review,node,'compass',root) + node['source']['file']='../escape.py';review['file']='../escape.py' + with self.assertRaises(ValueError):check_review(review,node,'compass',root) + + +if __name__ == '__main__': + unittest.main() From 30a55707ef651e2018cda159bc47669fa3c3896f Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 17:54:07 -0700 Subject: [PATCH 21/97] feat: explain hub connectivity by relation and direction --- CHANGELOG.md | 4 + COMPATIBILITY.md | 11 ++ crates/compass-graph/src/analyze.rs | 123 ++++++++++++++++ crates/compass-graph/src/lib.rs | 5 +- .../compass-graph/tests/analyze_coverage.rs | 76 ++++++++++ crates/compass-mcp/src/lib.rs | 93 ++++++++++-- crates/compass-mcp/tests/coverage_paths.rs | 5 + ...ode-graph-intelligence-audit-2026-09-26.md | 133 +++++++++++++++++- docs/reference/outputs.md | 20 +++ 9 files changed, 454 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6f07f10..8e5177943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Explain MCP hub candidates with node kind and bounded relation/direction + counts, preserving parallel records and distinguishing incident records from + ranking degree. Report omitted relation categories explicitly. + - Require unique exact endpoints for MCP paths and return ambiguity candidates instead of choosing by score. Explore within the requested hop bound using shared work limits, prefer structural relations among equal-hop paths, and diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 026448030..c599e0484 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -117,6 +117,17 @@ through the non-transport `invoke` helper. Graph schemas are unchanged. ### MCP hub identities +Each hub now also carries an additive `connectivity` object with schema +`compass.hub-connectivity/1`, and text includes its node kind and relation +breakdown. Hub eligibility, degree, and ranking are unchanged. Incident record +counts retain parallel records; they are not distinct-neighbor degree or +independently verified source occurrences. At most 16 relation categories are +shown, ordered by record count then name, with explicit omission totals. +Undirected artifacts do not acquire inferred directions in this summary. +Their `ranking` metadata now correctly says +`distinct-undirected-endpoint-degree`; directed artifacts retain +`distinct-directed-endpoint-degree`. + MCP `god_nodes` adds `structuredContent` using the existing `compass.mcp.tool-result/1` envelope and the result schema `compass.mcp.hubs/1`. The ranked records preserve exact IDs, kinds, degrees, diff --git a/crates/compass-graph/src/analyze.rs b/crates/compass-graph/src/analyze.rs index 9fb7ed3d0..6334bc032 100644 --- a/crates/compass-graph/src/analyze.rs +++ b/crates/compass-graph/src/analyze.rs @@ -60,6 +60,39 @@ pub struct GodNode { pub degree: usize, } +/// Stored incident records, separate from the distinct-pair hub ranking. +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HubRelation { + pub relation: String, + pub edge_records: usize, + pub incoming_records: usize, + pub outgoing_records: usize, + pub undirected_records: usize, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HubConnectivity { + pub schema: &'static str, + pub directed: bool, + pub edge_records: usize, + pub self_loop_records: usize, + pub relations: Vec, + pub omitted_relation_kinds: usize, + pub omitted_relation_records: usize, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HubEvidence { + pub hub: GodNode, + pub connectivity: HubConnectivity, +} + +pub const MAX_HUB_RELATIONS: usize = 16; +pub const HUB_CONNECTIVITY_SCHEMA: &str = "compass.hub-connectivity/1"; + #[derive(Clone, Debug, PartialEq, Eq, Serialize)] pub struct SurpriseConnection { pub source: String, @@ -218,6 +251,96 @@ pub fn god_nodes(document: &GraphDocument, top_n: usize) -> Vec { god_nodes_in(&graph, top_n) } +/// Explain the unchanged hub ranking using all valid incident edge records. +/// +/// Parallel records are retained. A directed self-loop contributes once to +/// `edge_records` and once to each direction; undirected records have no +/// inferred direction. Relation rows are bounded, with explicit omissions. +/// These graph observations do not establish source precision or design defects. +pub fn god_nodes_with_evidence(document: &GraphDocument, top_n: usize) -> Vec { + let graph = AnalysisGraph::new(document); + let ranked = god_nodes_in(&graph, top_n); + let selected = ranked + .iter() + .enumerate() + .map(|(index, hub)| (hub.id.as_str(), index)) + .collect::>(); + let mut relations = vec![BTreeMap::::new(); ranked.len()]; + let mut loops = vec![0; ranked.len()]; + for edge in &graph.relation_edges { + let record = edge.record; + for (endpoint, id) in [&record.source, &record.target].into_iter().enumerate() { + // Each self-loop is one incident record, not two records. + if endpoint == 1 && record.source == record.target { + continue; + } + if let Some(&index) = selected.get(id.as_str()) { + add_hub_relation(&mut relations[index], record, id, document.directed); + loops[index] += usize::from(record.source == record.target); + } + } + } + ranked + .into_iter() + .zip(relations) + .zip(loops) + .map(|((hub, relations), self_loop_records)| { + let mut relations = relations.into_values().collect::>(); + relations.sort_by(|left, right| { + right + .edge_records + .cmp(&left.edge_records) + .then_with(|| left.relation.cmp(&right.relation)) + }); + let edge_records = relations.iter().map(|row| row.edge_records).sum(); + let omitted_relation_kinds = relations.len().saturating_sub(MAX_HUB_RELATIONS); + let omitted_relation_records = relations + .iter() + .skip(MAX_HUB_RELATIONS) + .map(|row| row.edge_records) + .sum(); + relations.truncate(MAX_HUB_RELATIONS); + HubEvidence { + hub, + connectivity: HubConnectivity { + schema: HUB_CONNECTIVITY_SCHEMA, + directed: document.directed, + edge_records, + self_loop_records, + relations, + omitted_relation_kinds, + omitted_relation_records, + }, + } + }) + .collect() +} + +fn add_hub_relation( + relations: &mut BTreeMap, + record: &EdgeRecord, + id: &str, + directed: bool, +) { + let relation = record.string("relation"); + let row = relations + .entry(relation.clone()) + .or_insert_with(|| HubRelation { + relation, + edge_records: 0, + incoming_records: 0, + outgoing_records: 0, + undirected_records: 0, + }); + row.edge_records += 1; + if directed { + row.incoming_records += usize::from(record.target == id); + row.outgoing_records += usize::from(record.source == id); + } else { + row.undirected_records += 1; + } +} + fn god_nodes_in(graph: &AnalysisGraph<'_>, top_n: usize) -> Vec { let mut ranked = graph .nodes diff --git a/crates/compass-graph/src/lib.rs b/crates/compass-graph/src/lib.rs index ebcde1a76..357ece4cb 100644 --- a/crates/compass-graph/src/lib.rs +++ b/crates/compass-graph/src/lib.rs @@ -12,8 +12,9 @@ mod v1; pub use analyze::{ BlindSpotEdge, BlindSpotLimits, BlindSpotNode, BlindSpotOmissions, BlindSpotReport, CommunityGap, DiffEdge, DiffNode, DisconnectedComponent, GRAPH_INSIGHTS_SCHEMA, GodNode, - GraphDiff, GraphInsights, ImportCycle, SuggestedQuestion, SurpriseConnection, - blind_spot_report, find_import_cycles, god_nodes, graph_diff, graph_insights, + GraphDiff, GraphInsights, HUB_CONNECTIVITY_SCHEMA, HubConnectivity, HubEvidence, HubRelation, + ImportCycle, MAX_HUB_RELATIONS, SuggestedQuestion, SurpriseConnection, blind_spot_report, + find_import_cycles, god_nodes, god_nodes_with_evidence, graph_diff, graph_insights, graph_insights_with_blind_spots, suggest_questions, surprising_connections, }; pub use cluster::{ diff --git a/crates/compass-graph/tests/analyze_coverage.rs b/crates/compass-graph/tests/analyze_coverage.rs index 97bd3e8e6..d0f2b636a 100644 --- a/crates/compass-graph/tests/analyze_coverage.rs +++ b/crates/compass-graph/tests/analyze_coverage.rs @@ -600,3 +600,79 @@ fn diff_and_cycle_analysis_cover_add_remove_direction_deferred_and_rotation() { assert!(find_import_cycles(&cycle_graph, 2, 10).is_empty()); assert!(find_import_cycles(&document(Vec::new(), Vec::new(), true), 5, 10).is_empty()); } + +#[test] +fn hub_evidence_preserves_parallel_records_directions_and_self_loops() { + for directed in [true, false] { + let mut graph = document_with_multigraph( + vec![ + node("hub", "Hub", "src/hub.rs"), + node("peer", "Peer", "src/peer.rs"), + ], + vec![ + edge("hub", "peer", "calls", "EXTRACTED"), + edge("hub", "peer", "calls", "EXTRACTED"), + edge("peer", "hub", "references", "EXTRACTED"), + edge("hub", "hub", "calls", "EXTRACTED"), + edge("hub", "missing", "calls", "EXTRACTED"), + ], + directed, + true, + ); + let evidence = compass_graph::god_nodes_with_evidence(&graph, 10); + assert_eq!( + evidence.iter().map(|e| &e.hub).collect::>(), + god_nodes(&graph, 10).iter().collect::>() + ); + let hub = &evidence[0]; + assert_eq!(hub.hub.id, "hub"); + assert_eq!(hub.hub.degree, if directed { 4 } else { 3 }); + let c = &hub.connectivity; + assert_eq!(c.edge_records, 4); + assert_eq!(c.self_loop_records, 1); + assert_eq!(c.omitted_relation_records, 0); + assert_eq!(c.relations[0].relation, "calls"); + assert_eq!(c.relations[0].edge_records, 3); + assert_eq!(c.relations[0].incoming_records, usize::from(directed)); + assert_eq!( + c.relations[0].outgoing_records, + if directed { 3 } else { 0 } + ); + assert_eq!( + c.relations[0].undirected_records, + if directed { 0 } else { 3 } + ); + graph.links.reverse(); + graph.nodes.reverse(); + assert_eq!(evidence, compass_graph::god_nodes_with_evidence(&graph, 10)); + assert!(compass_graph::god_nodes_with_evidence(&graph, 0).is_empty()); + } +} + +#[test] +fn hub_evidence_bounds_relation_rows_without_hiding_omitted_records() { + let mut graph = document_with_multigraph( + vec![ + node("hub", "Hub", "src/hub.rs"), + node("peer", "Peer", "src/peer.rs"), + ], + (0..20) + .map(|i| edge("hub", "peer", &format!("r{i:02}"), "EXTRACTED")) + .collect(), + true, + true, + ); + graph.links.push(graph.links[19].clone()); + let evidence = compass_graph::god_nodes_with_evidence(&graph, 1); + let c = &evidence[0].connectivity; + assert_eq!(c.edge_records, 21); + assert_eq!(c.relations.len(), compass_graph::MAX_HUB_RELATIONS); + assert_eq!(c.relations[0].relation, "r19"); + assert_eq!(c.relations[1].relation, "r00"); + assert_eq!(c.omitted_relation_kinds, 4); + assert_eq!(c.omitted_relation_records, 4); + assert_eq!( + c.relations.iter().map(|r| r.edge_records).sum::() + c.omitted_relation_records, + c.edge_records + ); +} diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 36552463d..34bd842b3 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -21,7 +21,8 @@ use compass_agent_graph::{ }; use compass_core::{AgentGraphContext, LoadedGraph}; use compass_graph::{ - Communities, blind_spot_report, god_nodes, suggest_questions, surprising_connections, + Communities, blind_spot_report, god_nodes_with_evidence, suggest_questions, + surprising_connections, }; use compass_model::code_graph::GraphDocument as CodeGraphDocument; use compass_model::query_contract::{ @@ -1371,7 +1372,7 @@ fn tool_specs() -> Vec { ), tool( "god_nodes", - "Return connected hub candidates with exact IDs, source anchors, and degree. Connectivity is a topology observation, not proof of excessive responsibility.", + "Return connected hub candidates with exact IDs, source anchors, degree, node kind, and bounded incoming/outgoing relation-record counts. Connectivity is a topology observation, not proof of excessive responsibility.", json!({"type":"object","properties":{"top_n":{"type":"integer","default":10}}}), ), tool( @@ -2354,7 +2355,7 @@ fn invoke_hub_tool( let top_n = usize::try_from(integer_argument(arguments, "top_n", 10).max(0)).unwrap_or_default(); let document = context.document()?; - let nodes = god_nodes(&document, top_n); + let nodes = god_nodes_with_evidence(&document, top_n); let records = document .nodes .iter() @@ -2362,7 +2363,9 @@ fn invoke_hub_tool( .collect::>(); let mut lines = vec!["God nodes (most connected):".to_owned()]; let mut identities = Vec::with_capacity(nodes.len()); - for (index, node) in nodes.iter().enumerate() { + for (index, evidence) in nodes.iter().enumerate() { + let node = &evidence.hub; + let connectivity = &evidence.connectivity; let record = records.get(node.id.as_str()).ok_or_else(|| { InvocationError::Internal("hub identity is absent from the selected graph".to_owned()) })?; @@ -2380,15 +2383,45 @@ fn invoke_hub_tool( json!(record.source_file()), json!(source_location) )); + let kind = record.kind_name(); + lines.push(format!( + " kind: {} | incident records: {} | self-loops: {}", + if kind.is_empty() { + "unknown".to_owned() + } else { + sanitize_label(kind) + }, + connectivity.edge_records, + connectivity.self_loop_records + )); + for relation in &connectivity.relations { + lines.push(format!( + " relation {}: incoming {}, outgoing {}, undirected {}", + json!(relation.relation), + relation.incoming_records, + relation.outgoing_records, + relation.undirected_records + )); + } + if connectivity.omitted_relation_kinds > 0 { + lines.push(format!( + " {} additional relation kinds ({} records) omitted", + connectivity.omitted_relation_kinds, connectivity.omitted_relation_records + )); + } identities.push(json!({ "rank":index + 1, "id":node.id, "label":node.label, "degree":node.degree, "kind":record.kind_name(), "sourceFile":record.source_file(), "sourceLocation":source_location, "startLine":record.unsigned("line_start"), - "endLine":record.unsigned("line_end") + "endLine":record.unsigned("line_end"), "connectivity": connectivity })); } let structured = transport_envelope(json!({ - "schema":"compass.mcp.hubs/1", "ranking":"distinct-directed-endpoint-degree", + "schema":"compass.mcp.hubs/1", "ranking":if document.directed { + "distinct-directed-endpoint-degree" + } else { + "distinct-undirected-endpoint-degree" + }, "interpretation":"topology-candidates", "requested":top_n, "nodes":identities }))?; Ok(ToolInvocation { @@ -3035,6 +3068,33 @@ mod tests { Ok(()) } + #[test] + fn hub_evidence_does_not_invent_direction_for_undirected_graphs() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("graph.json"); + fs::write( + &path, + serde_json::to_vec(&json!({"directed":false, + "nodes":[{"id":"a","label":"A","source_file":"a.rs"}, + {"id":"b","label":"B","source_file":"b.rs"}], + "links":[{"source":"a","target":"b","relation":"calls"}]}))?, + )?; + let result = CompassMcp::new(path) + .invoke_result("god_nodes", &mut Map::new()) + .map_err(|error| error.to_string())?; + let content = result.structured_content.ok_or("missing hubs")?; + assert_eq!( + content["result"]["ranking"], + "distinct-undirected-endpoint-degree" + ); + let row = &content["result"]["nodes"][0]["connectivity"]["relations"][0]; + assert_eq!(row["incomingRecords"], 0); + assert_eq!(row["outgoingRecords"], 0); + assert_eq!(row["undirectedRecords"], 1); + Ok(()) + } + #[test] fn hub_results_preserve_exact_identity_and_source_anchors() -> Result<(), Box> { @@ -3073,6 +3133,19 @@ mod tests { assert_eq!(nodes.len(), 3); assert_eq!(nodes[0]["id"], "b"); assert_eq!(nodes[0]["degree"], 2); + assert_eq!( + nodes[0]["connectivity"]["schema"], + "compass.hub-connectivity/1" + ); + assert_eq!(nodes[0]["connectivity"]["edgeRecords"], 2); + assert_eq!( + nodes[0]["connectivity"]["relations"][0]["incomingRecords"], + 2 + ); + assert_eq!( + nodes[0]["connectivity"]["relations"][0]["outgoingRecords"], + 0 + ); let unusual = nodes .iter() .find(|node| node["id"] == unusual_id) @@ -3541,12 +3614,12 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null\n 2. Path - 1 edges\n id: \"z\" | source: \"src/path.rs\" | location: null" + "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null\n kind: symbol | incident records: 1 | self-loops: 0\n relation \"uses\": incoming 1, outgoing 0, undirected 0\n 2. Path - 1 edges\n id: \"z\" | source: \"src/path.rs\" | location: null\n kind: symbol | incident records: 1 | self-loops: 0\n relation \"uses\": incoming 0, outgoing 1, undirected 0" ); let arguments = Map::from_iter([("top_n".to_owned(), json!(1))]); assert_eq!( server.invoke("god_nodes", arguments), - "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null" + "God nodes (most connected):\n 1. Counter - 1 edges\n id: \"a\" | source: \"src/counter.rs\" | location: null\n kind: symbol | incident records: 1 | self-loops: 0\n relation \"uses\": incoming 1, outgoing 0, undirected 0" ); Ok(()) } @@ -3572,7 +3645,7 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. .dispatch() - 1 edges\n id: \"method\" | source: \"src/service.rs\" | location: \"L5\"" + "God nodes (most connected):\n 1. .dispatch() - 1 edges\n id: \"method\" | source: \"src/service.rs\" | location: \"L5\"\n kind: method | incident records: 1 | self-loops: 0\n relation \"contains\": incoming 1, outgoing 0, undirected 0" ); Ok(()) } @@ -3598,7 +3671,7 @@ mod tests { let server = CompassMcp::new(&graph); assert_eq!( server.invoke("god_nodes", Map::new()), - "God nodes (most connected):\n 1. helper() - 1 edges\n id: \"helper\" | source: \"src/support.sh\" | location: \"L1\"\n 2. prepare() - 1 edges\n id: \"prepare\" | source: \"bin/launch\" | location: \"L2\"" + "God nodes (most connected):\n 1. helper() - 1 edges\n id: \"helper\" | source: \"src/support.sh\" | location: \"L1\"\n kind: function | incident records: 1 | self-loops: 0\n relation \"calls\": incoming 1, outgoing 0, undirected 0\n 2. prepare() - 1 edges\n id: \"prepare\" | source: \"bin/launch\" | location: \"L2\"\n kind: function | incident records: 1 | self-loops: 0\n relation \"calls\": incoming 0, outgoing 1, undirected 0" ); Ok(()) } diff --git a/crates/compass-mcp/tests/coverage_paths.rs b/crates/compass-mcp/tests/coverage_paths.rs index 70f8e5ab7..753c2c511 100644 --- a/crates/compass-mcp/tests/coverage_paths.rs +++ b/crates/compass-mcp/tests/coverage_paths.rs @@ -313,6 +313,11 @@ async fn in_memory_protocol_exercises_tool_and_resource_server_handlers() let structured = hubs.structured_content.ok_or("missing structured hubs")?; assert_eq!(structured["result"]["schema"], "compass.mcp.hubs/1"); assert!(structured["result"]["nodes"].as_array().is_some()); + assert_eq!( + structured["result"]["nodes"][0]["connectivity"]["schema"], + "compass.hub-connectivity/1" + ); + assert!(structured["result"]["nodes"][0]["connectivity"]["edgeRecords"].is_u64()); let path = client .call_tool( CallToolRequestParams::new("shortest_path") diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 957566f17..1777afee5 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -872,15 +872,140 @@ warnings. Full extraction fixture qualification remains the earlier receiver checkpoint: this correction changes query/MCP behavior on retained graphs, not extraction. Concurrent debug replays are not timing evidence. +## Hub explanations and source-role census + +The next development diagnostic inspects the original 100 MCP hub entries, +50 per tool. `benchmarks/agent_query/hub_role_reviews.json` records manual +source-role judgments, exact graph IDs when recoverable from the response, +pinned source-file hashes, anchors, and excerpts. It deliberately follows +output inspection. It is neither preregistered design-quality evaluation nor a +representative precision sample. Different returned sets prevent a shared +accuracy score. + +The independent `hub_evidence_audit.py` verifies the original capture and source +provenance. It checks 87 reviewed identities against the pinned source; the 13 +ambiguous Graphify labels remain unknown. This verifies where the judgments +came from, not that a source hash can prove a semantic role or design judgment. +The original report is `hub-source-role-audit-01.json`. + +| Reviewed role among returned hubs | Compass | Graphify | +| --- | ---: | ---: | +| Production type declaration | 21 | 19 | +| Production callable | 12 | 4 | +| Test helper callable | 6 | 5 | +| Test suite type | 0 | 3 | +| Whole production source module | 6 | 0 | +| Whole test module | 4 | 0 | +| Example callable | 1 | 1 | +| Benchmark callable | 0 | 1 | +| Generic implementation target | 0 | 2 | +| Trait implementation block | 0 | 2 | +| Unidentified from returned label | 0 | 13 | + +Types include classes, structs, aliases, interfaces, and public testing API +types. “Production” distinguishes the source declaration from repository test +helpers, not its release stability or architectural importance. All ten +Compass Zod results are whole-file module nodes, four spanning test files. +These can be useful file navigation hubs, but do not identify ten god objects. +Graphify's Axum `S` and `T` entries are generic implementation subjects, and +`Router` and `Bytes` refer to trait implementation blocks rather than new +type declarations. Earlier source diagnostics establish two wrong references +to `S`; this census does not extrapolate their frequency. + +### Exposing why a hub is connected + +Both original MCP hub responses primarily state degree. Compass now adds +stored node kind and a bounded relation/direction breakdown, in text and +`connectivity` with schema `compass.hub-connectivity/1`. Analysis lives in +`compass-graph`; MCP projects it. Eligibility, degree, and ranking stay the +same. Undirected ranking metadata is corrected to name its existing policy. +The summary counts all valid incident records, including parallel records, +separately from distinct-pair degree. Directed self-loops count once in the +incident total and once per direction. Undirected records receive no invented +arrow. Sixteen relation categories are shown by descending record count then +name, with omitted category and record totals. + +Concrete observations from the retained Compass graphs illustrate why this +matters: + +- Cobra `Command`: 525 incident records, including 346 incoming references and + 154 outgoing containment records. Its degree is 443, a different quantity. +- Flask `Flask`: 110 records include 38 outgoing containment and 34 incoming + references; several other relation kinds contribute too. +- Gson `JsonReader`: 530 records include 240 incoming instantiations and 97 + outgoing containment records. +- Zod `to-json-schema.test`: 1,156 records include 789 containment, 321 + references, and 46 calls, all outgoing. It is a whole test module. +- Axum `.route()`: 584 records include 306 incoming calls, one outgoing call, + and 272 incoming `tests` records. A high degree does not mean this method + makes hundreds of outgoing calls. + +These counts are observations about the stored graph, not independently +verified counts of source occurrences or evidence of excessive responsibility. +The previous neighbor interface groups by neighbor and can omit parallel +relations; a summary should not be reconstructed from that grouped view. +The native implementation uses complete stored records. No automatic +production/test role classifier or god-object judgment is introduced. + +The summary auditor separately checks numeric types, direction, self-loops, +parallel records, ordering, omission accounting, and each hub's text block. +Original Compass and Graphify responses have no such summary. That absence is +reported as unavailable, not as a wrong answer or lack of another workflow. +Graphify's neighbor and CLI interfaces remain available; this diagnostic does +not measure their multi-step explanation cost or quality. + +### Hub explanation replay and verification + +`mcp-hub-evidence-01` completes all 58 original shared MCP questions on the +same graphs. All original graph-consistency judgments and all returned hub +identity/degree/rank records remain unchanged. The independently verified +`hub-evidence-audit-01.json` checks **50/50 Compass connectivity summaries and +50/50 matching text blocks**. Graphify's direct hub responses offer no such +summary; this is recorded as unavailable, not 50 incorrect explanations. +The latest source-role check (`hub-source-role-audit-02.json`) also verifies +each hub RPC's raw request and response. Graphify's stored graphs are +undirected, so their oracle record counts remain undirected; the summary does +not infer semantic directions from their serialized endpoints. + +For the same five Compass top-ten hub responses, text grows from 9,136 to +26,620 bytes and captured response-wire bytes grow from 24,146 to 75,299. +Graphify remains at 1,489 text / 1,989 wire bytes. This is additional evidence +with a payload cost, not an efficiency win or a controlled multi-step workflow +comparison. Initialization traffic is retained separately. Ranking sets differ +between products, and these runs do not establish comparative design quality. + +The frozen executable in `hub-evidence-provenance` has SHA256 +`5382b51b86ab76032173a17e638a3ad525600ec1726364d3244edddb7abd2c19`; +the collector records the same executable digest. Base commit, source patch, +and changed-source hashes are retained. Native verification passes **1,162 +tests, zero failed, two ignored**, covering the full workspace library/binary +baseline plus `analyze_coverage`, `coverage_paths`, and `compass_product`. +This is a different integration selection from the prior path checkpoint, +whose larger count also included CLI/output path suites. New regressions +cover parallel edges, self-loops, missing endpoints, directed/undirected +records, permutation stability, top-zero, relation omission totals, text +projection, and actual MCP transport. Workspace and selected integration +Clippy pass with warnings denied. All **93 Python tests**, formatting, diff +checks, and product boundary pass. Logs: `hub-evidence-native-04.log`, +`hub-evidence-clippy-03.log`, and `hub-evidence-python-03.log`. + +Earlier development logs preserve a test fixture type mismatch and an expected +legacy-kind mismatch (`symbol`, not `unknown`); both were corrected before the +final checks. Existing core `unused_mut` and macOS linker unwind warnings remain +in test builds. The full extraction fixture gate remains the prior receiver +checkpoint: no extractor, publication, or viewer format changes are made here. +God-object detection, community cohesion, source-edge precision, and explanation +usefulness on held-out tasks remain open. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. -2. Expand the now-identifiable hubs into source-reviewed role and design - judgments, including containment-dominated modules and generic reference - targets. Evaluate cluster responsibilities and cross-community connections - separately from graph consistency. +2. Use the source-role census and connectivity breakdowns to review actual + responsibilities and source-edge correctness, including containment-heavy + modules and generic reference targets. Evaluate cluster responsibilities + and cross-community connections separately from graph consistency. 3. Extend the development navigation-path judgments to directed call paths, longer walks, parallel source occurrences, broader ambiguity/unreachable cases, and real-repository work exhaustion. A negative or limit outcome diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 7dc048008..06a898804 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -850,6 +850,26 @@ it does not establish directed call flow or execution feasibility. ### MCP hub results +Each node's additive `connectivity` object uses `compass.hub-connectivity/1`: + +- `directed` reports the stored graph's direction mode. +- `edgeRecords` counts all valid incident records once each, retaining parallel + records. It can differ from the distinct-pair ranking degree. This includes + valid document containment records excluded from topology weighting. +- `selfLoopRecords` counts incident records whose endpoints are the hub itself. +- `relations` contains at most 16 rows, ordered by descending `edgeRecords` + then lexical `relation`. Each row carries `edgeRecords`, `incomingRecords`, + `outgoingRecords`, and `undirectedRecords`. Directed self-loops count once + in each direction but once in the total. An undirected graph contributes + only undirected counts. An absent relation remains an empty string. +- `omittedRelationKinds` and `omittedRelationRecords` account for excluded + relation rows; displayed record counts plus omitted records equal the total. + +Text includes the stored node kind and this breakdown. These observations help +distinguish containment, incoming use, and outgoing dependencies. They do not +infer responsibility count, source correctness, or a god-object design defect. +No test/production role is inferred automatically from a path or label. + MCP `god_nodes` returns human-readable text and a structured projection in `structuredContent.result`, inside `compass.mcp.tool-result/1`: From 7c70fbeaf78818f1b5055b3a852c0f6350728e2f Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 18:01:54 -0700 Subject: [PATCH 22/97] bench: register held-out five-language confirmation panel --- benchmarks/agent_query/COVERAGE_PLAN.md | 21 + benchmarks/agent_query/README.md | 15 + .../edge_witnesses_heldout_chi.json | 102 +++ .../edge_witnesses_heldout_click.json | 102 +++ .../edge_witnesses_heldout_jsoup.json | 107 +++ .../edge_witnesses_heldout_redux.json | 127 +++ .../edge_witnesses_heldout_walkdir.json | 107 +++ benchmarks/agent_query/heldout_panel_a.json | 117 +++ .../agent_query/path_witnesses_heldout_a.json | 396 +++++++++ benchmarks/agent_query/suite_heldout_a.toml | 834 ++++++++++++++++++ 10 files changed, 1928 insertions(+) create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_chi.json create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_click.json create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_jsoup.json create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_redux.json create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_walkdir.json create mode 100644 benchmarks/agent_query/heldout_panel_a.json create mode 100644 benchmarks/agent_query/path_witnesses_heldout_a.json create mode 100644 benchmarks/agent_query/suite_heldout_a.toml diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 708481062..7eef88257 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -13,6 +13,27 @@ are recorded in `suite_v2.toml`, `suite_fd.toml`, and each captured run. Use additional independently selected repositories for confirmation after improving on these development cases. Do not relabel used questions as held out. +### Confirmation panel A + +`suite_heldout_a.toml` selects Chi, Click, jsoup, Redux, and WalkDir as additional +small-library repositories across the same five languages. Selection is +purposive, not random or representative. Commits and reviewed-file hashes are +recorded in `heldout_panel_a.json`; the Compass binary is frozen at the prior +hub-evidence checkpoint. No output on these repositories is used to select +questions. Commit all 55 questions, 21 edge-pair witnesses (18 positive call +occurrences plus five direct-edge negatives), and ten forward/reverse path +witnesses before extracting or querying either product. + +Each repository covers two declaration lookups, callers, callees, forward and +reverse navigation, file connectivity, ambiguity, missing symbols, bounded +natural query, and an incoming-call `ask` task. The CLI runner reports text +recall proxies. Independent source/graph audits must check identity, relation, +direction, and occurrence multiplicity separately. A negative edge needs both +endpoints resolved; missing extraction cannot pass as absence. Source roles, +MCP/community workflows, directed paths, and broader design judgments remain +separate work. Preserve first-run results; after tuning on this panel, treat it +as development and select another holdout for confirmation. + ## Question and evidence matrix Each new question must record its exact source witness, expected outcome, diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index a21a4cd5e..d2c8e7353 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -228,6 +228,21 @@ cap terminates the process group and fails the observation; truncated text is never scored as a successful response. An invalid Compass snapshot pointer fails preparation instead of selecting an arbitrary unpublished snapshot. +## Held-out confirmation panel A + +`suite_heldout_a.toml` contains 55 source-reviewed CLI questions on five new +pinned repositories. `heldout_panel_a.json` records selection scope, source +hashes, and the frozen Compass build. Run with the normal `runner run --suite` +interface and explicit `--source NAME=PATH` for Chi, Click, jsoup, Redux, and +WalkDir; keep all generated artifacts outside the source checkouts. + +Run `edge_audit` separately with each `edge_witnesses_heldout_NAME.json`, and +`path_audit --witnesses benchmarks/agent_query/path_witnesses_heldout_a.json` on +the captured run. These witnesses are registered before either tool executes +the panel. The question score remains a text-recall proxy; report independent +identity/direction/occurrence checks and all failures separately. Repository +selection is purposive, so this does not estimate population accuracy. + ## Shared MCP comparison `suite_mcp.json` preregisters 29 questions per tool across the same five-language diff --git a/benchmarks/agent_query/edge_witnesses_heldout_chi.json b/benchmarks/agent_query/edge_witnesses_heldout_chi.json new file mode 100644 index 000000000..6eea529af --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_chi.json @@ -0,0 +1,102 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "scope": "Pre-execution source-reviewed selected direct edges and occurrences on held-out panel A; no model output used as gold, no whole-repository precision/recall claim.", + "witnesses": [ + { + "id": "chi-source-edge-1", + "source": { + "file": "chi.go", + "line": 62, + "symbol": "NewRouter", + "text": "func NewRouter() *Mux {" + }, + "target": { + "file": "mux.go", + "line": 52, + "symbol": "NewMux", + "text": "func NewMux() *Mux {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "chi.go", + "line": 63, + "text": "return NewMux()" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-2", + "source": { + "file": "context.go", + "line": 16, + "symbol": "URLParamFromCtx", + "text": "func URLParamFromCtx(ctx context.Context, key string) string {" + }, + "target": { + "file": "context.go", + "line": 25, + "symbol": "RouteContext", + "text": "func RouteContext(ctx context.Context) *Context {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "context.go", + "line": 17, + "text": "if rctx := RouteContext(ctx); rctx != nil {" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-3", + "source": { + "file": "context.go", + "line": 16, + "symbol": "URLParamFromCtx", + "text": "func URLParamFromCtx(ctx context.Context, key string) string {" + }, + "target": { + "file": "context.go", + "line": 124, + "symbol": "URLParam", + "text": "func (x *Context) URLParam(key string) string {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "context.go", + "line": 18, + "text": "return rctx.URLParam(key)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-4", + "source": { + "file": "chi.go", + "line": 62, + "symbol": "NewRouter", + "text": "func NewRouter() *Mux {" + }, + "target": { + "file": "context.go", + "line": 31, + "symbol": "NewRouteContext", + "text": "func NewRouteContext() *Context {" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ] +} diff --git a/benchmarks/agent_query/edge_witnesses_heldout_click.json b/benchmarks/agent_query/edge_witnesses_heldout_click.json new file mode 100644 index 000000000..a5bc347b7 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_click.json @@ -0,0 +1,102 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "scope": "Pre-execution source-reviewed selected direct edges and occurrences on held-out panel A; no model output used as gold, no whole-repository precision/recall claim.", + "witnesses": [ + { + "id": "click-source-edge-1", + "source": { + "file": "src/click/utils.py", + "line": 381, + "symbol": "open_file", + "text": "def open_file(" + }, + "target": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-2", + "source": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "target": { + "file": "src/click/_compat.py", + "line": 361, + "symbol": "_wrap_io_open", + "text": "def _wrap_io_open(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/_compat.py", + "line": 397, + "text": "return _wrap_io_open(filename, mode, encoding, errors), True" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-3", + "source": { + "file": "src/click/utils.py", + "line": 153, + "symbol": "open", + "text": "def open(self) -> t.IO[t.Any]:" + }, + "target": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/utils.py", + "line": 161, + "text": "rv, self.should_close = open_stream(" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-4", + "source": { + "file": "src/click/utils.py", + "line": 381, + "symbol": "open_file", + "text": "def open_file(" + }, + "target": { + "file": "src/click/utils.py", + "line": 430, + "symbol": "format_filename", + "text": "def format_filename(" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ] +} diff --git a/benchmarks/agent_query/edge_witnesses_heldout_jsoup.json b/benchmarks/agent_query/edge_witnesses_heldout_jsoup.json new file mode 100644 index 000000000..12288ea6c --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_jsoup.json @@ -0,0 +1,107 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "scope": "Pre-execution source-reviewed selected direct edges and occurrences on held-out panel A; no model output used as gold, no whole-repository precision/recall claim.", + "witnesses": [ + { + "id": "jsoup-source-edge-1", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "symbol": "isValidBodyHtml", + "text": "public boolean isValidBodyHtml(String bodyHtml) {" + }, + "target": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 182, + "symbol": "copySafeNodes", + "text": "private int copySafeNodes(Element source, Element dest) {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 131, + "text": "int numDiscarded = copySafeNodes(dirty.body(), clean.body());" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "jsoup-source-edge-2", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "symbol": "isValidBodyHtml", + "text": "public boolean isValidBodyHtml(String bodyHtml) {" + }, + "target": { + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 65, + "symbol": "createShell", + "text": "public static Document createShell(String baseUri) {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 126, + "text": "Document clean = Document.createShell(baseUri);" + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 127, + "text": "Document dirty = Document.createShell(baseUri);" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "jsoup-source-edge-3", + "source": { + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 434, + "symbol": "isValid", + "text": "public static boolean isValid(String bodyHtml, Safelist safelist) {" + }, + "target": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "symbol": "isValidBodyHtml", + "text": "public boolean isValidBodyHtml(String bodyHtml) {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 435, + "text": "return new Cleaner(safelist).isValidBodyHtml(bodyHtml);" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "jsoup-source-edge-4", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "symbol": "isValidBodyHtml", + "text": "public boolean isValidBodyHtml(String bodyHtml) {" + }, + "target": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 62, + "symbol": "clean", + "text": "public Document clean(Document dirtyDocument) {" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ] +} diff --git a/benchmarks/agent_query/edge_witnesses_heldout_redux.json b/benchmarks/agent_query/edge_witnesses_heldout_redux.json new file mode 100644 index 000000000..b7506e2f5 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_redux.json @@ -0,0 +1,127 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "scope": "Pre-execution source-reviewed selected direct edges and occurrences on held-out panel A; no model output used as gold, no whole-repository precision/recall claim.", + "witnesses": [ + { + "id": "redux-source-edge-1", + "source": { + "file": "src/utils/kindOf.ts", + "line": 2, + "symbol": "miniKindOf", + "text": "export function miniKindOf(val: any): string {" + }, + "target": { + "file": "src/utils/kindOf.ts", + "line": 40, + "symbol": "ctorName", + "text": "function ctorName(val: any): string | null {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "redux-source-edge-2", + "source": { + "file": "src/utils/kindOf.ts", + "line": 2, + "symbol": "miniKindOf", + "text": "export function miniKindOf(val: any): string {" + }, + "target": { + "file": "src/utils/kindOf.ts", + "line": 53, + "symbol": "isDate", + "text": "function isDate(val: any) {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/utils/kindOf.ts", + "line": 18, + "text": "if (isDate(val)) return 'date'" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "redux-source-edge-3", + "source": { + "file": "src/utils/isAction.ts", + "line": 4, + "symbol": "isAction", + "text": "export default function isAction(action: unknown): action is Action {" + }, + "target": { + "file": "src/utils/isPlainObject.ts", + "line": 5, + "symbol": "isPlainObject", + "text": "export default function isPlainObject(obj: any): obj is object {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/utils/isAction.ts", + "line": 6, + "text": "isPlainObject(action) &&" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "redux-source-edge-4", + "source": { + "file": "src/utils/kindOf.ts", + "line": 62, + "symbol": "kindOf", + "text": "export function kindOf(val: any) {" + }, + "target": { + "file": "src/utils/kindOf.ts", + "line": 44, + "symbol": "isError", + "text": "function isError(val: any) {" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "redux-source-edge-5", + "source": { + "file": "src/utils/kindOf.ts", + "line": 62, + "symbol": "kindOf", + "text": "export function kindOf(val: any) {" + }, + "target": { + "file": "src/utils/kindOf.ts", + "line": 2, + "symbol": "miniKindOf", + "text": "export function miniKindOf(val: any): string {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/utils/kindOf.ts", + "line": 66, + "text": "typeOfVal = miniKindOf(val)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ] +} diff --git a/benchmarks/agent_query/edge_witnesses_heldout_walkdir.json b/benchmarks/agent_query/edge_witnesses_heldout_walkdir.json new file mode 100644 index 000000000..366d46668 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_walkdir.json @@ -0,0 +1,107 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "scope": "Pre-execution source-reviewed selected direct edges and occurrences on held-out panel A; no model output used as gold, no whole-repository precision/recall claim.", + "witnesses": [ + { + "id": "walkdir-source-edge-1", + "source": { + "file": "src/lib.rs", + "line": 439, + "symbol": "sort_by_key", + "text": "pub fn sort_by_key(self, mut cmp: F) -> Self" + }, + "target": { + "file": "src/lib.rs", + "line": 417, + "symbol": "sort_by", + "text": "pub fn sort_by(mut self, cmp: F) -> Self" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/lib.rs", + "line": 444, + "text": "self.sort_by(move |a, b| cmp(a).cmp(&cmp(b)))" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "walkdir-source-edge-2", + "source": { + "file": "src/lib.rs", + "line": 456, + "symbol": "sort_by_file_name", + "text": "pub fn sort_by_file_name(self) -> Self {" + }, + "target": { + "file": "src/lib.rs", + "line": 417, + "symbol": "sort_by", + "text": "pub fn sort_by(mut self, cmp: F) -> Self" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/lib.rs", + "line": 457, + "text": "self.sort_by(|a, b| a.file_name().cmp(b.file_name()))" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "walkdir-source-edge-3", + "source": { + "file": "src/lib.rs", + "line": 687, + "symbol": "next", + "text": "fn next(&mut self) -> Option> {" + }, + "target": { + "file": "src/lib.rs", + "line": 950, + "symbol": "pop", + "text": "fn pop(&mut self) {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/lib.rs", + "line": 707, + "text": "self.pop();" + }, + { + "file": "src/lib.rs", + "line": 718, + "text": "None => self.pop()," + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "walkdir-source-edge-4", + "source": { + "file": "src/lib.rs", + "line": 456, + "symbol": "sort_by_file_name", + "text": "pub fn sort_by_file_name(self) -> Self {" + }, + "target": { + "file": "src/lib.rs", + "line": 439, + "symbol": "sort_by_key", + "text": "pub fn sort_by_key(self, mut cmp: F) -> Self" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ] +} diff --git a/benchmarks/agent_query/heldout_panel_a.json b/benchmarks/agent_query/heldout_panel_a.json new file mode 100644 index 000000000..5403ef703 --- /dev/null +++ b/benchmarks/agent_query/heldout_panel_a.json @@ -0,0 +1,117 @@ +{ + "schema": "compass.heldout-panel/1", + "scope": "Purposive confirmation panel, not a representative population sample. No outputs from either tool on these repositories informed question selection. After results are inspected, any tuning on this panel is development and needs another holdout.", + "repositories": [ + { + "name": "chi", + "language": "Go", + "url": "https://github.com/go-chi/chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "reviewedFiles": [ + { + "file": "chi.go", + "sha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678" + }, + { + "file": "context.go", + "sha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53" + }, + { + "file": "mux.go", + "sha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + } + ] + }, + { + "name": "click", + "language": "Python", + "url": "https://github.com/pallets/click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "reviewedFiles": [ + { + "file": "src/click/_compat.py", + "sha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + { + "file": "src/click/utils.py", + "sha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17" + } + ] + }, + { + "name": "jsoup", + "language": "Java", + "url": "https://github.com/jhy/jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "reviewedFiles": [ + { + "file": "src/main/java/org/jsoup/Jsoup.java", + "sha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7" + }, + { + "file": "src/main/java/org/jsoup/nodes/Document.java", + "sha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502" + }, + { + "file": "src/main/java/org/jsoup/parser/Parser.java", + "sha256": "2b8baa95140fbf12fad9c874b42d8d31e24b186fd7c665c1708395e6d852ba98" + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "sha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + } + ] + }, + { + "name": "redux", + "language": "TypeScript", + "url": "https://github.com/reduxjs/redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "reviewedFiles": [ + { + "file": "src/createStore.ts", + "sha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695" + }, + { + "file": "src/utils/isAction.ts", + "sha256": "2cf7a3b535cb14b9bb2ac7648b5f103f2a80427d96c7eb391fd11f617944a8a2" + }, + { + "file": "src/utils/isPlainObject.ts", + "sha256": "30153dae9fd245b8574f96cf9c08cf33e7d12881b0ad4e4e458646bef5b32793" + }, + { + "file": "src/utils/kindOf.ts", + "sha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + } + ] + }, + { + "name": "walkdir", + "language": "Rust", + "url": "https://github.com/BurntSushi/walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "reviewedFiles": [ + { + "file": "src/dent.rs", + "sha256": "ca573f4533370a09851579f5940f7cd9bd121b2f30ec51d29a40afdce984683b" + }, + { + "file": "src/error.rs", + "sha256": "ba58bf6f59d196567435d4b66699a928cc237fc7c8df01dc37ab822509905b7c" + }, + { + "file": "src/lib.rs", + "sha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a" + } + ] + } + ], + "productBinarySha256": "5382b51b86ab76032173a17e638a3ad525600ec1726364d3244edddb7abd2c19", + "productSourceCommit": "30a55707ef651e2018cda159bc47669fa3c3896f", + "surfacesPending": [ + "Held-out MCP hubs and community workflows", + "Directed path and long-walk tasks", + "Independent functional cohesion and god-object judgments" + ] +} diff --git a/benchmarks/agent_query/path_witnesses_heldout_a.json b/benchmarks/agent_query/path_witnesses_heldout_a.json new file mode 100644 index 000000000..fe9bf38d5 --- /dev/null +++ b/benchmarks/agent_query/path_witnesses_heldout_a.json @@ -0,0 +1,396 @@ +{ + "schema": "compass.agent-path-witnesses/1", + "scope": "Pre-execution source-reviewed one-hop forward/reverse witnesses for held-out panel A. Checks exact rendered identities, direction, relation, and at least one correct occurrence; not runtime execution or whole-graph precision.", + "witnesses": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "question": "chi-holdout-path-forward", + "category": "call", + "nodes": [ + { + "file": "chi.go", + "line": 62, + "text": "func NewRouter() *Mux {", + "labels": [ + "NewRouter()", + ".NewRouter()" + ] + }, + { + "file": "mux.go", + "line": 52, + "text": "func NewMux() *Mux {", + "labels": [ + "NewMux()", + ".NewMux()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "chi.go", + "line": 63, + "text": "return NewMux()" + } + } + ] + }, + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "question": "chi-holdout-path-reverse", + "category": "call", + "nodes": [ + { + "file": "mux.go", + "line": 52, + "text": "func NewMux() *Mux {", + "labels": [ + "NewMux()", + ".NewMux()" + ] + }, + { + "file": "chi.go", + "line": 62, + "text": "func NewRouter() *Mux {", + "labels": [ + "NewRouter()", + ".NewRouter()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "reverse", + "site": { + "file": "chi.go", + "line": 63, + "text": "return NewMux()" + } + } + ] + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "question": "click-holdout-path-forward", + "category": "call", + "nodes": [ + { + "file": "src/click/utils.py", + "line": 381, + "text": "def open_file(", + "labels": [ + "open_file()", + ".open_file()" + ] + }, + { + "file": "src/click/_compat.py", + "line": 374, + "text": "def open_stream(", + "labels": [ + "open_stream()", + ".open_stream()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)" + } + } + ] + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "question": "click-holdout-path-reverse", + "category": "call", + "nodes": [ + { + "file": "src/click/_compat.py", + "line": 374, + "text": "def open_stream(", + "labels": [ + "open_stream()", + ".open_stream()" + ] + }, + { + "file": "src/click/utils.py", + "line": 381, + "text": "def open_file(", + "labels": [ + "open_file()", + ".open_file()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "reverse", + "site": { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)" + } + } + ] + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "question": "jsoup-holdout-path-forward", + "category": "call", + "nodes": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "text": "public boolean isValidBodyHtml(String bodyHtml) {", + "labels": [ + "isValidBodyHtml()", + ".isValidBodyHtml()" + ] + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 182, + "text": "private int copySafeNodes(Element source, Element dest) {", + "labels": [ + "copySafeNodes()", + ".copySafeNodes()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 131, + "text": "int numDiscarded = copySafeNodes(dirty.body(), clean.body());" + } + } + ] + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "question": "jsoup-holdout-path-reverse", + "category": "call", + "nodes": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 182, + "text": "private int copySafeNodes(Element source, Element dest) {", + "labels": [ + "copySafeNodes()", + ".copySafeNodes()" + ] + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "text": "public boolean isValidBodyHtml(String bodyHtml) {", + "labels": [ + "isValidBodyHtml()", + ".isValidBodyHtml()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "reverse", + "site": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 131, + "text": "int numDiscarded = copySafeNodes(dirty.body(), clean.body());" + } + } + ] + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "question": "redux-holdout-path-forward", + "category": "call", + "nodes": [ + { + "file": "src/utils/kindOf.ts", + "line": 2, + "text": "export function miniKindOf(val: any): string {", + "labels": [ + "miniKindOf()", + ".miniKindOf()" + ] + }, + { + "file": "src/utils/kindOf.ts", + "line": 40, + "text": "function ctorName(val: any): string | null {", + "labels": [ + "ctorName()", + ".ctorName()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)" + } + } + ] + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "question": "redux-holdout-path-reverse", + "category": "call", + "nodes": [ + { + "file": "src/utils/kindOf.ts", + "line": 40, + "text": "function ctorName(val: any): string | null {", + "labels": [ + "ctorName()", + ".ctorName()" + ] + }, + { + "file": "src/utils/kindOf.ts", + "line": 2, + "text": "export function miniKindOf(val: any): string {", + "labels": [ + "miniKindOf()", + ".miniKindOf()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "reverse", + "site": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)" + } + } + ] + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "question": "walkdir-holdout-path-forward", + "category": "call", + "nodes": [ + { + "file": "src/lib.rs", + "line": 456, + "text": "pub fn sort_by_file_name(self) -> Self {", + "labels": [ + "sort_by_file_name()", + ".sort_by_file_name()" + ] + }, + { + "file": "src/lib.rs", + "line": 417, + "text": "pub fn sort_by(mut self, cmp: F) -> Self", + "labels": [ + "sort_by()", + ".sort_by()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "forward", + "site": { + "file": "src/lib.rs", + "line": 457, + "text": "self.sort_by(|a, b| a.file_name().cmp(b.file_name()))" + } + } + ] + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "question": "walkdir-holdout-path-reverse", + "category": "call", + "nodes": [ + { + "file": "src/lib.rs", + "line": 417, + "text": "pub fn sort_by(mut self, cmp: F) -> Self", + "labels": [ + "sort_by()", + ".sort_by()" + ] + }, + { + "file": "src/lib.rs", + "line": 456, + "text": "pub fn sort_by_file_name(self) -> Self {", + "labels": [ + "sort_by_file_name()", + ".sort_by_file_name()" + ] + } + ], + "steps": [ + { + "relations": [ + "calls" + ], + "direction": "reverse", + "site": { + "file": "src/lib.rs", + "line": 457, + "text": "self.sort_by(|a, b| a.file_name().cmp(b.file_name()))" + } + } + ] + } + ] +} diff --git a/benchmarks/agent_query/suite_heldout_a.toml b/benchmarks/agent_query/suite_heldout_a.toml new file mode 100644 index 000000000..67d77621e --- /dev/null +++ b/benchmarks/agent_query/suite_heldout_a.toml @@ -0,0 +1,834 @@ +# Held-out confirmation panel A. Product binaries are frozen before this panel. +# Questions and source witnesses are committed before either tool builds or queries these repositories. +# Purposively chosen small libraries, one per existing language; not a random or representative sample. +# Primary verdicts are text recall proxies. Separate edge/path audits retain exact identities and source sites. +# Callers/affected and callees/explain are corresponding public operations, not identical relation-filter interfaces. +# Paths are undirected for both tools. All failures, ambiguities, and bound outcomes remain in the denominator. +# Broad query pages start at 600 estimated tokens, with up to three documented follow-ups; ask rows request 2000. +schema = "compass.agent-query-suite/1" + +[[repository]] +name = "chi" +language = "Go" +url = "https://github.com/go-chi/chi.git" +commit = "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc" + +[[repository.anchor]] +file = "chi.go" +line = 62 +symbol = "NewRouter" +judgment = "Source declaration: func NewRouter() *Mux {" + +[[repository.anchor]] +file = "context.go" +line = 11 +symbol = "URLParam" +judgment = "Source declaration: func URLParam(r *http.Request, key string) string {" + +[[repository.anchor]] +file = "context.go" +line = 16 +symbol = "URLParamFromCtx" +judgment = "Source declaration: func URLParamFromCtx(ctx context.Context, key string) string {" + +[[repository.anchor]] +file = "context.go" +line = 25 +symbol = "RouteContext" +judgment = "Source declaration: func RouteContext(ctx context.Context) *Context {" + +[[repository.anchor]] +file = "context.go" +line = 31 +symbol = "NewRouteContext" +judgment = "Source declaration: func NewRouteContext() *Context {" + +[[repository.anchor]] +file = "context.go" +line = 124 +symbol = "URLParam" +judgment = "Source declaration: func (x *Context) URLParam(key string) string {" + +[[repository.anchor]] +file = "mux.go" +line = 52 +symbol = "NewMux" +judgment = "Source declaration: func NewMux() *Mux {" + +[[repository.question]] +id = "chi-holdout-explain-primary" +kind = "explain" +subject = "Locate NewRouter declaration" +compass = ["explain", "NewRouter"] +graphify = ["explain", "NewRouter"] +expect = "answer" +required = ["chi.go", "62"] +judgment = "chi.go:62: func NewRouter() *Mux {" + +[[repository.question]] +id = "chi-holdout-explain-target" +kind = "explain" +subject = "Locate NewMux declaration" +compass = ["explain", "NewMux"] +graphify = ["explain", "NewMux"] +expect = "answer" +required = ["mux.go", "52"] +judgment = "mux.go:52: func NewMux() *Mux {" + +[[repository.question]] +id = "chi-holdout-callers" +kind = "callers" +subject = "Direct callers of NewMux" +compass = ["callers", "NewMux"] +graphify = ["affected", "NewMux", "--depth", "1"] +expect = "answer" +required = ["NewRouter", "chi.go"] +judgment = "NewRouter directly calls NewMux; source occurrences are recorded separately." + +[[repository.question]] +id = "chi-holdout-callees" +kind = "callees" +subject = "Direct internal callees of NewRouter" +compass = ["callees", "NewRouter"] +graphify = ["explain", "NewRouter"] +expect = "answer" +required = ["NewMux"] +judgment = "Reviewed lexical body calls; source witnesses disambiguate selected endpoints." + +[[repository.question]] +id = "chi-holdout-path-forward" +kind = "path" +subject = "Undirected navigation from NewRouter to NewMux" +compass = ["path", "NewRouter", "NewMux"] +graphify = ["path", "NewRouter", "NewMux", "--undirected"] +expect = "answer" +required = ["NewRouter", "NewMux"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at chi.go:63; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "chi-holdout-path-reverse" +kind = "path" +subject = "Undirected navigation from NewMux to NewRouter" +compass = ["path", "NewMux", "NewRouter"] +graphify = ["path", "NewMux", "NewRouter", "--undirected"] +expect = "answer" +required = ["NewMux", "NewRouter"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at chi.go:63; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "chi-holdout-file-path" +kind = "file_path" +subject = "Connect chi.go and mux.go" +compass = ["path", "chi.go", "mux.go"] +graphify = ["path", "chi.go", "mux.go", "--undirected"] +expect = "answer" +required = ["chi.go", "mux.go"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "The reviewed files contain a direct internal call/import or re-export relationship; no particular intermediate file route is prescribed." + +[[repository.question]] +id = "chi-holdout-ambiguity" +kind = "ambiguity" +subject = "Distinguish same-named URLParam declarations" +compass = ["search", "URLParam"] +graphify = ["explain", "URLParam"] +expect = "pick_list" +required_one_of = ["context.go"] +min_one_of = 1 +min_candidates = 2 +judgment = "Distinct declaration anchors are recorded for this unqualified name; require candidates rather than silently choosing." + +[[repository.question]] +id = "chi-holdout-negative" +kind = "negative" +subject = "Reject absent exact symbol HeldoutAbsentChiSymbol8197" +compass = ["search", "HeldoutAbsentChiSymbol8197"] +graphify = ["explain", "HeldoutAbsentChiSymbol8197"] +expect = "no_match" +judgment = "The exact sentinel is absent from the pinned checkout; a convenient fuzzy substitute is not a correct answer." + +[[repository.question]] +id = "chi-holdout-broad" +kind = "broad" +subject = "how is a new router created" +compass = ["query", "how is a new router created"] +graphify = ["query", "how is a new router created"] +expect = "answer" +required = ["NewRouter"] +budget_tokens = 600 +max_follow_ups = 3 +judgment = "The reviewed primary declaration implements this task. Required name presence is only a recall proxy, not whole-answer correctness." + +[[repository.question]] +id = "chi-holdout-ask-callers" +kind = "callers" +subject = "who calls NewMux" +compass = ["ask", "who calls NewMux", "--text-budget", "2000"] +graphify = ["query", "who calls NewMux", "--budget", "2000"] +expect = "answer" +required = ["NewRouter", "chi.go"] +judgment = "Same incoming-call task as direct callers, through each product natural-language entry point; separately inspect subject and direction before a correctness claim." + +[[repository]] +name = "click" +language = "Python" +url = "https://github.com/pallets/click.git" +commit = "06b2a678741131fd577ce170e23e5ca0aeba0309" + +[[repository.anchor]] +file = "src/click/_compat.py" +line = 361 +symbol = "_wrap_io_open" +judgment = "Source declaration: def _wrap_io_open(" + +[[repository.anchor]] +file = "src/click/_compat.py" +line = 374 +symbol = "open_stream" +judgment = "Source declaration: def open_stream(" + +[[repository.anchor]] +file = "src/click/_compat.py" +line = 466 +symbol = "close" +judgment = "Source declaration: def close(self, delete: bool = False) -> None:" + +[[repository.anchor]] +file = "src/click/utils.py" +line = 153 +symbol = "open" +judgment = "Source declaration: def open(self) -> t.IO[t.Any]:" + +[[repository.anchor]] +file = "src/click/utils.py" +line = 171 +symbol = "close" +judgment = "Source declaration: def close(self) -> None:" + +[[repository.anchor]] +file = "src/click/utils.py" +line = 381 +symbol = "open_file" +judgment = "Source declaration: def open_file(" + +[[repository.anchor]] +file = "src/click/utils.py" +line = 430 +symbol = "format_filename" +judgment = "Source declaration: def format_filename(" + +[[repository.question]] +id = "click-holdout-explain-primary" +kind = "explain" +subject = "Locate open_file declaration" +compass = ["explain", "open_file"] +graphify = ["explain", "open_file"] +expect = "answer" +required = ["src/click/utils.py", "381"] +judgment = "src/click/utils.py:381: def open_file(" + +[[repository.question]] +id = "click-holdout-explain-target" +kind = "explain" +subject = "Locate open_stream declaration" +compass = ["explain", "open_stream"] +graphify = ["explain", "open_stream"] +expect = "answer" +required = ["src/click/_compat.py", "374"] +judgment = "src/click/_compat.py:374: def open_stream(" + +[[repository.question]] +id = "click-holdout-callers" +kind = "callers" +subject = "Direct callers of open_stream" +compass = ["callers", "open_stream"] +graphify = ["affected", "open_stream", "--depth", "1"] +expect = "answer" +required = ["open_file", "utils.py"] +judgment = "open_file directly calls open_stream; source occurrences are recorded separately." + +[[repository.question]] +id = "click-holdout-callees" +kind = "callees" +subject = "Direct internal callees of open_file" +compass = ["callees", "open_file"] +graphify = ["explain", "open_file"] +expect = "answer" +required = ["open_stream"] +judgment = "Reviewed lexical body calls; source witnesses disambiguate selected endpoints." + +[[repository.question]] +id = "click-holdout-path-forward" +kind = "path" +subject = "Undirected navigation from open_file to open_stream" +compass = ["path", "open_file", "open_stream"] +graphify = ["path", "open_file", "open_stream", "--undirected"] +expect = "answer" +required = ["open_file", "open_stream"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/click/utils.py:422; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "click-holdout-path-reverse" +kind = "path" +subject = "Undirected navigation from open_stream to open_file" +compass = ["path", "open_stream", "open_file"] +graphify = ["path", "open_stream", "open_file", "--undirected"] +expect = "answer" +required = ["open_stream", "open_file"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/click/utils.py:422; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "click-holdout-file-path" +kind = "file_path" +subject = "Connect src/click/utils.py and src/click/_compat.py" +compass = ["path", "src/click/utils.py", "src/click/_compat.py"] +graphify = ["path", "src/click/utils.py", "src/click/_compat.py", "--undirected"] +expect = "answer" +required = ["utils.py", "_compat.py"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "The reviewed files contain a direct internal call/import or re-export relationship; no particular intermediate file route is prescribed." + +[[repository.question]] +id = "click-holdout-ambiguity" +kind = "ambiguity" +subject = "Distinguish same-named close declarations" +compass = ["search", "close"] +graphify = ["explain", "close"] +expect = "pick_list" +required_one_of = ["utils.py", "_compat.py"] +min_one_of = 1 +min_candidates = 2 +judgment = "Distinct declaration anchors are recorded for this unqualified name; require candidates rather than silently choosing." + +[[repository.question]] +id = "click-holdout-negative" +kind = "negative" +subject = "Reject absent exact symbol HeldoutAbsentClickSymbol8197" +compass = ["search", "HeldoutAbsentClickSymbol8197"] +graphify = ["explain", "HeldoutAbsentClickSymbol8197"] +expect = "no_match" +judgment = "The exact sentinel is absent from the pinned checkout; a convenient fuzzy substitute is not a correct answer." + +[[repository.question]] +id = "click-holdout-broad" +kind = "broad" +subject = "how are files opened with lazy or atomic writes" +compass = ["query", "how are files opened with lazy or atomic writes"] +graphify = ["query", "how are files opened with lazy or atomic writes"] +expect = "answer" +required = ["open_file"] +budget_tokens = 600 +max_follow_ups = 3 +judgment = "The reviewed primary declaration implements this task. Required name presence is only a recall proxy, not whole-answer correctness." + +[[repository.question]] +id = "click-holdout-ask-callers" +kind = "callers" +subject = "who calls open_stream" +compass = ["ask", "who calls open_stream", "--text-budget", "2000"] +graphify = ["query", "who calls open_stream", "--budget", "2000"] +expect = "answer" +required = ["open_file", "utils.py"] +judgment = "Same incoming-call task as direct callers, through each product natural-language entry point; separately inspect subject and direction before a correctness claim." + +[[repository]] +name = "jsoup" +language = "Java" +url = "https://github.com/jhy/jsoup.git" +commit = "37aea49902972cec9a53dc2c65023729f1c3715b" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/Jsoup.java" +line = 434 +symbol = "isValid" +judgment = "Source declaration: public static boolean isValid(String bodyHtml, Safelist safelist) {" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/nodes/Document.java" +line = 65 +symbol = "createShell" +judgment = "Source declaration: public static Document createShell(String baseUri) {" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/safety/Cleaner.java" +line = 62 +symbol = "clean" +judgment = "Source declaration: public Document clean(Document dirtyDocument) {" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/safety/Cleaner.java" +line = 94 +symbol = "isValid" +judgment = "Source declaration: public boolean isValid(Document dirtyDocument) {" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/safety/Cleaner.java" +line = 124 +symbol = "isValidBodyHtml" +judgment = "Source declaration: public boolean isValidBodyHtml(String bodyHtml) {" + +[[repository.anchor]] +file = "src/main/java/org/jsoup/safety/Cleaner.java" +line = 182 +symbol = "copySafeNodes" +judgment = "Source declaration: private int copySafeNodes(Element source, Element dest) {" + +[[repository.question]] +id = "jsoup-holdout-explain-primary" +kind = "explain" +subject = "Locate isValidBodyHtml declaration" +compass = ["explain", "isValidBodyHtml"] +graphify = ["explain", "isValidBodyHtml"] +expect = "answer" +required = ["src/main/java/org/jsoup/safety/Cleaner.java", "124"] +judgment = "src/main/java/org/jsoup/safety/Cleaner.java:124: public boolean isValidBodyHtml(String bodyHtml) {" + +[[repository.question]] +id = "jsoup-holdout-explain-target" +kind = "explain" +subject = "Locate copySafeNodes declaration" +compass = ["explain", "copySafeNodes"] +graphify = ["explain", "copySafeNodes"] +expect = "answer" +required = ["src/main/java/org/jsoup/safety/Cleaner.java", "182"] +judgment = "src/main/java/org/jsoup/safety/Cleaner.java:182: private int copySafeNodes(Element source, Element dest) {" + +[[repository.question]] +id = "jsoup-holdout-callers" +kind = "callers" +subject = "Direct callers of copySafeNodes" +compass = ["callers", "copySafeNodes"] +graphify = ["affected", "copySafeNodes", "--depth", "1"] +expect = "answer" +required = ["isValidBodyHtml", "Cleaner.java"] +judgment = "isValidBodyHtml directly calls copySafeNodes; source occurrences are recorded separately." + +[[repository.question]] +id = "jsoup-holdout-callees" +kind = "callees" +subject = "Direct internal callees of isValidBodyHtml" +compass = ["callees", "isValidBodyHtml"] +graphify = ["explain", "isValidBodyHtml"] +expect = "answer" +required = ["copySafeNodes", "createShell"] +judgment = "Reviewed lexical body calls; source witnesses disambiguate selected endpoints." + +[[repository.question]] +id = "jsoup-holdout-path-forward" +kind = "path" +subject = "Undirected navigation from isValidBodyHtml to copySafeNodes" +compass = ["path", "isValidBodyHtml", "copySafeNodes"] +graphify = ["path", "isValidBodyHtml", "copySafeNodes", "--undirected"] +expect = "answer" +required = ["isValidBodyHtml", "copySafeNodes"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/main/java/org/jsoup/safety/Cleaner.java:131; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "jsoup-holdout-path-reverse" +kind = "path" +subject = "Undirected navigation from copySafeNodes to isValidBodyHtml" +compass = ["path", "copySafeNodes", "isValidBodyHtml"] +graphify = ["path", "copySafeNodes", "isValidBodyHtml", "--undirected"] +expect = "answer" +required = ["copySafeNodes", "isValidBodyHtml"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/main/java/org/jsoup/safety/Cleaner.java:131; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "jsoup-holdout-file-path" +kind = "file_path" +subject = "Connect src/main/java/org/jsoup/safety/Cleaner.java and src/main/java/org/jsoup/parser/Parser.java" +compass = ["path", "src/main/java/org/jsoup/safety/Cleaner.java", "src/main/java/org/jsoup/parser/Parser.java"] +graphify = ["path", "src/main/java/org/jsoup/safety/Cleaner.java", "src/main/java/org/jsoup/parser/Parser.java", "--undirected"] +expect = "answer" +required = ["Cleaner.java", "Parser.java"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "The reviewed files contain a direct internal call/import or re-export relationship; no particular intermediate file route is prescribed." + +[[repository.question]] +id = "jsoup-holdout-ambiguity" +kind = "ambiguity" +subject = "Distinguish same-named isValid declarations" +compass = ["search", "isValid"] +graphify = ["explain", "isValid"] +expect = "pick_list" +required_one_of = ["Cleaner.java", "Jsoup.java"] +min_one_of = 1 +min_candidates = 2 +judgment = "Distinct declaration anchors are recorded for this unqualified name; require candidates rather than silently choosing." + +[[repository.question]] +id = "jsoup-holdout-negative" +kind = "negative" +subject = "Reject absent exact symbol HeldoutAbsentJsoupSymbol8197" +compass = ["search", "HeldoutAbsentJsoupSymbol8197"] +graphify = ["explain", "HeldoutAbsentJsoupSymbol8197"] +expect = "no_match" +judgment = "The exact sentinel is absent from the pinned checkout; a convenient fuzzy substitute is not a correct answer." + +[[repository.question]] +id = "jsoup-holdout-broad" +kind = "broad" +subject = "how is body HTML validated against a safelist" +compass = ["query", "how is body HTML validated against a safelist"] +graphify = ["query", "how is body HTML validated against a safelist"] +expect = "answer" +required = ["isValidBodyHtml"] +budget_tokens = 600 +max_follow_ups = 3 +judgment = "The reviewed primary declaration implements this task. Required name presence is only a recall proxy, not whole-answer correctness." + +[[repository.question]] +id = "jsoup-holdout-ask-callers" +kind = "callers" +subject = "who calls copySafeNodes" +compass = ["ask", "who calls copySafeNodes", "--text-budget", "2000"] +graphify = ["query", "who calls copySafeNodes", "--budget", "2000"] +expect = "answer" +required = ["isValidBodyHtml", "Cleaner.java"] +judgment = "Same incoming-call task as direct callers, through each product natural-language entry point; separately inspect subject and direction before a correctness claim." + +[[repository]] +name = "redux" +language = "TypeScript" +url = "https://github.com/reduxjs/redux.git" +commit = "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e" + +[[repository.anchor]] +file = "src/createStore.ts" +line = 201 +symbol = "subscribe" +judgment = "Source declaration: function subscribe(listener: () => void) {" + +[[repository.anchor]] +file = "src/createStore.ts" +line = 355 +symbol = "subscribe" +judgment = "Source declaration: subscribe(observer: unknown) {" + +[[repository.anchor]] +file = "src/utils/isAction.ts" +line = 4 +symbol = "isAction" +judgment = "Source declaration: export default function isAction(action: unknown): action is Action {" + +[[repository.anchor]] +file = "src/utils/isPlainObject.ts" +line = 5 +symbol = "isPlainObject" +judgment = "Source declaration: export default function isPlainObject(obj: any): obj is object {" + +[[repository.anchor]] +file = "src/utils/kindOf.ts" +line = 2 +symbol = "miniKindOf" +judgment = "Source declaration: export function miniKindOf(val: any): string {" + +[[repository.anchor]] +file = "src/utils/kindOf.ts" +line = 40 +symbol = "ctorName" +judgment = "Source declaration: function ctorName(val: any): string | null {" + +[[repository.anchor]] +file = "src/utils/kindOf.ts" +line = 44 +symbol = "isError" +judgment = "Source declaration: function isError(val: any) {" + +[[repository.anchor]] +file = "src/utils/kindOf.ts" +line = 53 +symbol = "isDate" +judgment = "Source declaration: function isDate(val: any) {" + +[[repository.anchor]] +file = "src/utils/kindOf.ts" +line = 62 +symbol = "kindOf" +judgment = "Source declaration: export function kindOf(val: any) {" + +[[repository.question]] +id = "redux-holdout-explain-primary" +kind = "explain" +subject = "Locate miniKindOf declaration" +compass = ["explain", "miniKindOf"] +graphify = ["explain", "miniKindOf"] +expect = "answer" +required = ["src/utils/kindOf.ts", "2"] +judgment = "src/utils/kindOf.ts:2: export function miniKindOf(val: any): string {" + +[[repository.question]] +id = "redux-holdout-explain-target" +kind = "explain" +subject = "Locate ctorName declaration" +compass = ["explain", "ctorName"] +graphify = ["explain", "ctorName"] +expect = "answer" +required = ["src/utils/kindOf.ts", "40"] +judgment = "src/utils/kindOf.ts:40: function ctorName(val: any): string | null {" + +[[repository.question]] +id = "redux-holdout-callers" +kind = "callers" +subject = "Direct callers of ctorName" +compass = ["callers", "ctorName"] +graphify = ["affected", "ctorName", "--depth", "1"] +expect = "answer" +required = ["miniKindOf", "kindOf.ts"] +judgment = "miniKindOf directly calls ctorName; source occurrences are recorded separately." + +[[repository.question]] +id = "redux-holdout-callees" +kind = "callees" +subject = "Direct internal callees of miniKindOf" +compass = ["callees", "miniKindOf"] +graphify = ["explain", "miniKindOf"] +expect = "answer" +required = ["isDate", "isError", "ctorName"] +judgment = "Reviewed lexical body calls; source witnesses disambiguate selected endpoints." + +[[repository.question]] +id = "redux-holdout-path-forward" +kind = "path" +subject = "Undirected navigation from miniKindOf to ctorName" +compass = ["path", "miniKindOf", "ctorName"] +graphify = ["path", "miniKindOf", "ctorName", "--undirected"] +expect = "answer" +required = ["miniKindOf", "ctorName"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/utils/kindOf.ts:21; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "redux-holdout-path-reverse" +kind = "path" +subject = "Undirected navigation from ctorName to miniKindOf" +compass = ["path", "ctorName", "miniKindOf"] +graphify = ["path", "ctorName", "miniKindOf", "--undirected"] +expect = "answer" +required = ["ctorName", "miniKindOf"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/utils/kindOf.ts:21; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "redux-holdout-file-path" +kind = "file_path" +subject = "Connect src/utils/isAction.ts and src/utils/isPlainObject.ts" +compass = ["path", "src/utils/isAction.ts", "src/utils/isPlainObject.ts"] +graphify = ["path", "src/utils/isAction.ts", "src/utils/isPlainObject.ts", "--undirected"] +expect = "answer" +required = ["isAction.ts", "isPlainObject.ts"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "The reviewed files contain a direct internal call/import or re-export relationship; no particular intermediate file route is prescribed." + +[[repository.question]] +id = "redux-holdout-ambiguity" +kind = "ambiguity" +subject = "Distinguish same-named subscribe declarations" +compass = ["search", "subscribe"] +graphify = ["explain", "subscribe"] +expect = "pick_list" +required_one_of = ["createStore.ts"] +min_one_of = 1 +min_candidates = 2 +judgment = "Distinct declaration anchors are recorded for this unqualified name; require candidates rather than silently choosing." + +[[repository.question]] +id = "redux-holdout-negative" +kind = "negative" +subject = "Reject absent exact symbol HeldoutAbsentReduxSymbol8197" +compass = ["search", "HeldoutAbsentReduxSymbol8197"] +graphify = ["explain", "HeldoutAbsentReduxSymbol8197"] +expect = "no_match" +judgment = "The exact sentinel is absent from the pinned checkout; a convenient fuzzy substitute is not a correct answer." + +[[repository.question]] +id = "redux-holdout-broad" +kind = "broad" +subject = "how is the kind of a value detected" +compass = ["query", "how is the kind of a value detected"] +graphify = ["query", "how is the kind of a value detected"] +expect = "answer" +required = ["miniKindOf"] +budget_tokens = 600 +max_follow_ups = 3 +judgment = "The reviewed primary declaration implements this task. Required name presence is only a recall proxy, not whole-answer correctness." + +[[repository.question]] +id = "redux-holdout-ask-callers" +kind = "callers" +subject = "who calls ctorName" +compass = ["ask", "who calls ctorName", "--text-budget", "2000"] +graphify = ["query", "who calls ctorName", "--budget", "2000"] +expect = "answer" +required = ["miniKindOf", "kindOf.ts"] +judgment = "Same incoming-call task as direct callers, through each product natural-language entry point; separately inspect subject and direction before a correctness claim." + +[[repository]] +name = "walkdir" +language = "Rust" +url = "https://github.com/BurntSushi/walkdir.git" +commit = "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec" + +[[repository.anchor]] +file = "src/dent.rs" +line = 77 +symbol = "path" +judgment = "Source declaration: pub fn path(&self) -> &Path {" + +[[repository.anchor]] +file = "src/error.rs" +line = 46 +symbol = "path" +judgment = "Source declaration: pub fn path(&self) -> Option<&Path> {" + +[[repository.anchor]] +file = "src/lib.rs" +line = 417 +symbol = "sort_by" +judgment = "Source declaration: pub fn sort_by(mut self, cmp: F) -> Self" + +[[repository.anchor]] +file = "src/lib.rs" +line = 439 +symbol = "sort_by_key" +judgment = "Source declaration: pub fn sort_by_key(self, mut cmp: F) -> Self" + +[[repository.anchor]] +file = "src/lib.rs" +line = 456 +symbol = "sort_by_file_name" +judgment = "Source declaration: pub fn sort_by_file_name(self) -> Self {" + +[[repository.anchor]] +file = "src/lib.rs" +line = 687 +symbol = "next" +judgment = "Source declaration: fn next(&mut self) -> Option> {" + +[[repository.anchor]] +file = "src/lib.rs" +line = 950 +symbol = "pop" +judgment = "Source declaration: fn pop(&mut self) {" + +[[repository.question]] +id = "walkdir-holdout-explain-primary" +kind = "explain" +subject = "Locate sort_by_file_name declaration" +compass = ["explain", "sort_by_file_name"] +graphify = ["explain", "sort_by_file_name"] +expect = "answer" +required = ["src/lib.rs", "456"] +judgment = "src/lib.rs:456: pub fn sort_by_file_name(self) -> Self {" + +[[repository.question]] +id = "walkdir-holdout-explain-target" +kind = "explain" +subject = "Locate sort_by declaration" +compass = ["explain", "sort_by"] +graphify = ["explain", "sort_by"] +expect = "answer" +required = ["src/lib.rs", "417"] +judgment = "src/lib.rs:417: pub fn sort_by(mut self, cmp: F) -> Self" + +[[repository.question]] +id = "walkdir-holdout-callers" +kind = "callers" +subject = "Direct callers of sort_by" +compass = ["callers", "sort_by"] +graphify = ["affected", "sort_by", "--depth", "1"] +expect = "answer" +required = ["sort_by_file_name", "lib.rs"] +judgment = "sort_by_file_name directly calls sort_by; source occurrences are recorded separately." + +[[repository.question]] +id = "walkdir-holdout-callees" +kind = "callees" +subject = "Direct internal callees of sort_by_file_name" +compass = ["callees", "sort_by_file_name"] +graphify = ["explain", "sort_by_file_name"] +expect = "answer" +required = ["sort_by"] +judgment = "Reviewed lexical body calls; source witnesses disambiguate selected endpoints." + +[[repository.question]] +id = "walkdir-holdout-path-forward" +kind = "path" +subject = "Undirected navigation from sort_by_file_name to sort_by" +compass = ["path", "sort_by_file_name", "sort_by"] +graphify = ["path", "sort_by_file_name", "sort_by", "--undirected"] +expect = "answer" +required = ["sort_by_file_name", "sort_by"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/lib.rs:457; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "walkdir-holdout-path-reverse" +kind = "path" +subject = "Undirected navigation from sort_by to sort_by_file_name" +compass = ["path", "sort_by", "sort_by_file_name"] +graphify = ["path", "sort_by", "sort_by_file_name", "--undirected"] +expect = "answer" +required = ["sort_by", "sort_by_file_name"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "A direct call occurs at src/lib.rs:457; reverse traversal must preserve the stored call direction." + +[[repository.question]] +id = "walkdir-holdout-file-path" +kind = "file_path" +subject = "Connect src/lib.rs and src/dent.rs" +compass = ["path", "src/lib.rs", "src/dent.rs"] +graphify = ["path", "src/lib.rs", "src/dent.rs", "--undirected"] +expect = "answer" +required = ["lib.rs", "dent.rs"] +forbidden = ["NO PATH FOUND", "No path found", "No directed path found"] +judgment = "The reviewed files contain a direct internal call/import or re-export relationship; no particular intermediate file route is prescribed." + +[[repository.question]] +id = "walkdir-holdout-ambiguity" +kind = "ambiguity" +subject = "Distinguish same-named path declarations" +compass = ["search", "path"] +graphify = ["explain", "path"] +expect = "pick_list" +required_one_of = ["dent.rs", "error.rs"] +min_one_of = 1 +min_candidates = 2 +judgment = "Distinct declaration anchors are recorded for this unqualified name; require candidates rather than silently choosing." + +[[repository.question]] +id = "walkdir-holdout-negative" +kind = "negative" +subject = "Reject absent exact symbol HeldoutAbsentWalkdirSymbol8197" +compass = ["search", "HeldoutAbsentWalkdirSymbol8197"] +graphify = ["explain", "HeldoutAbsentWalkdirSymbol8197"] +expect = "no_match" +judgment = "The exact sentinel is absent from the pinned checkout; a convenient fuzzy substitute is not a correct answer." + +[[repository.question]] +id = "walkdir-holdout-broad" +kind = "broad" +subject = "how are directory entries sorted by file name" +compass = ["query", "how are directory entries sorted by file name"] +graphify = ["query", "how are directory entries sorted by file name"] +expect = "answer" +required = ["sort_by_file_name"] +budget_tokens = 600 +max_follow_ups = 3 +judgment = "The reviewed primary declaration implements this task. Required name presence is only a recall proxy, not whole-answer correctness." + +[[repository.question]] +id = "walkdir-holdout-ask-callers" +kind = "callers" +subject = "who calls sort_by" +compass = ["ask", "who calls sort_by", "--text-budget", "2000"] +graphify = ["query", "who calls sort_by", "--budget", "2000"] +expect = "answer" +required = ["sort_by_file_name", "lib.rs"] +judgment = "Same incoming-call task as direct callers, through each product natural-language entry point; separately inspect subject and direction before a correctness claim." From 7c859f27df98aa7adc8e01e1f98441aa9c9c26f0 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 18:14:16 -0700 Subject: [PATCH 23/97] bench: audit frozen confirmation panel and preserve oracle corrections --- benchmarks/agent_query/README.md | 9 ++ ...dge_witnesses_heldout_click_corrected.json | 108 ++++++++++++++++ .../agent_query/heldout_panel_a_review.json | 118 ++++++++++++++++++ benchmarks/agent_query/path_audit.py | 27 +++- .../agent_query/tests/test_path_audit.py | 24 +++- ...ode-graph-intelligence-audit-2026-09-26.md | 106 ++++++++++++++++ 6 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 benchmarks/agent_query/edge_witnesses_heldout_click_corrected.json create mode 100644 benchmarks/agent_query/heldout_panel_a_review.json diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index d2c8e7353..38eccfdff 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -243,6 +243,15 @@ the panel. The question score remains a text-recall proxy; report independent identity/direction/occurrence checks and all failures separately. Repository selection is purposive, so this does not estimate population accuracy. +The first frozen results and post-output review are recorded in +`heldout_panel_a_review.json` and the main code-graph intelligence audit report. +Keep the original Click edge witness: its missing second `_wrap_io_open` site +is corrected only in `edge_witnesses_heldout_click_corrected.json`. Report both +registered and corrected diagnostic scores. The path auditor retains nonzero, +timed-out, and unsupported multi-response executions as failed rows; it still +rejects source/graph provenance drift. Later product tuning on panel A is +**development**, not another held-out confirmation. + ## Shared MCP comparison `suite_mcp.json` preregisters 29 questions per tool across the same five-language diff --git a/benchmarks/agent_query/edge_witnesses_heldout_click_corrected.json b/benchmarks/agent_query/edge_witnesses_heldout_click_corrected.json new file mode 100644 index 000000000..00943f747 --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_heldout_click_corrected.json @@ -0,0 +1,108 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "scope": "Post-output diagnostic correction to the registered held-out panel A Click witness. The original manifest remains unchanged. Full source-body review found a second open_stream -> _wrap_io_open call at line 450; this is an oracle correction, not a preregistered result.", + "witnesses": [ + { + "id": "click-source-edge-1", + "source": { + "file": "src/click/utils.py", + "line": 381, + "symbol": "open_file", + "text": "def open_file(" + }, + "target": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-2", + "source": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "target": { + "file": "src/click/_compat.py", + "line": 361, + "symbol": "_wrap_io_open", + "text": "def _wrap_io_open(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/_compat.py", + "line": 397, + "text": "return _wrap_io_open(filename, mode, encoding, errors), True" + }, + { + "file": "src/click/_compat.py", + "line": 450, + "text": "f = _wrap_io_open(fd, mode, encoding, errors)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-3", + "source": { + "file": "src/click/utils.py", + "line": 153, + "symbol": "open", + "text": "def open(self) -> t.IO[t.Any]:" + }, + "target": { + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "text": "def open_stream(" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "src/click/utils.py", + "line": 161, + "text": "rv, self.should_close = open_stream(" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "click-source-edge-4", + "source": { + "file": "src/click/utils.py", + "line": 381, + "symbol": "open_file", + "text": "def open_file(" + }, + "target": { + "file": "src/click/utils.py", + "line": 430, + "symbol": "format_filename", + "text": "def format_filename(" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + } + ], + "correctionOf": "edge_witnesses_heldout_click.json" +} diff --git a/benchmarks/agent_query/heldout_panel_a_review.json b/benchmarks/agent_query/heldout_panel_a_review.json new file mode 100644 index 000000000..833e499d0 --- /dev/null +++ b/benchmarks/agent_query/heldout_panel_a_review.json @@ -0,0 +1,118 @@ +{ + "askReview": "Compass uniquely targets the requested callee and prints incoming relationship direction on Chi, Click, jsoup, and Redux; WalkDir remains ambiguous. Incoming usages include references/imports where shown. Graphify prints broader neighborhoods containing the requested incoming call on these tasks; WalkDir combines test and library seeds. This is selected-fact recall, not whole-answer precision.", + "auditDigests": { + "heldout-a-chi-edge-audit-01.json": "fc4bac5f24224eb2f8308f3fcd1ad3ab0d5f3bfd5503916a73681d2a4c5eaadc", + "heldout-a-click-edge-audit-01.json": "4c81cb59f7a0d51caa7acc4bd692a0ae92dd976405fa9465924259767caace0c", + "heldout-a-click-edge-audit-corrected-01.json": "2cf35f85a402d5cdcbb88820409d0f3dee1ae41d2dc05951ded74d59d7fc6865", + "heldout-a-jsoup-edge-audit-01.json": "f6c1fff6a8763e01651cf600674f3a2abbc5172d460d58eff6ca4bc7683e1838", + "heldout-a-path-audit-01.json": "4b4e19d5ac3a977cbb2ce4f27ef4e72ec4c6e9f3f8c890de5ada7998d69f25a0", + "heldout-a-redux-edge-audit-01.json": "504f66e12c798cde7bc1010f1476678dc7e447f42da20b13a7d0d20fdb4382af", + "heldout-a-walkdir-edge-audit-01.json": "f611d41d07752835ecac23080974c5d4b0ef3ddcf8b73ffe6a9335777b8bce9b" + }, + "buildsSucceeded": 10, + "edgeSummary": { + "corrected": { + "compass": { + "fullOccurrenceMatches": 15, + "pairs": 21, + "positiveOccurrencesExpected": 19, + "positiveOccurrencesMatched": 14, + "relationshipMatches": 15, + "uniqueEndpointPairs": 17 + }, + "graphify": { + "fullOccurrenceMatches": 17, + "pairs": 21, + "positiveOccurrencesExpected": 19, + "positiveOccurrencesMatched": 15, + "relationshipMatches": 20, + "uniqueEndpointPairs": 21 + } + }, + "registered": { + "compass": { + "fullOccurrenceMatches": 14, + "pairs": 21, + "positiveOccurrencesExpected": 18, + "positiveOccurrencesMatched": 13, + "relationshipMatches": 15, + "uniqueEndpointPairs": 17 + }, + "graphify": { + "fullOccurrenceMatches": 18, + "pairs": 21, + "positiveOccurrencesExpected": 18, + "positiveOccurrencesMatched": 15, + "relationshipMatches": 20, + "uniqueEndpointPairs": 21 + } + } + }, + "inputDigests": { + "edge_witnesses_heldout_chi.json": "198b3982981429b4f96366685c1a32c5fdff295441da8a452cd351b620e779b4", + "edge_witnesses_heldout_click.json": "84657bfad12322347e0c7af4642897acbf572fe20d16f20fbcba489e958a22f6", + "edge_witnesses_heldout_jsoup.json": "e8bf60d682228a7115603049177349b399eed055b41743c35c6a638aaf24fc38", + "edge_witnesses_heldout_redux.json": "f0a361aa2a3492b8c816bd6351a4160ecd197348b474498683932e800ac7a7d2", + "edge_witnesses_heldout_walkdir.json": "1e203696db152499f7b08bfea40aa089d4b235ee744e76c2421f8c617f23b58a", + "heldout_panel_a.json": "e6d341f45f93a47ea297cd0f732d8a527e1d8de2f2ca1a0149760024d5c06c5c", + "path_witnesses_heldout_a.json": "fb7fcc2cfa698a0d64b1a0929a1a9a0f5c97e973deb05e303d798f0d0c033f6e", + "suite_heldout_a.toml": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237" + }, + "observations": 110, + "oracleCorrection": "Registered Click open_stream -> _wrap_io_open omitted the call at _compat.py:450. Corrected diagnostic adds it without replacing original witness or results.", + "pending": [ + "Whole-answer/source-edge precision", + "MCP hubs and community workflows on this panel", + "Functional cohesion and god-object judgments", + "Directed and long paths", + "Representative independent confirmation after further tuning" + ], + "provenanceLimit": "CLI run records executable hashes and reported versions; the Graphify launcher hash does not pin all Python package/dependency bytes during execution. The separate MCP package manifest must not be presented as a CLI pre/post package check.", + "registrationCommit": "7c70fbeaf78818f1b5055b3a852c0f6350728e2f", + "responseReviews": [ + { + "judgment": "Text pass is an ambiguity list for export and function nodes at the same declaration, not an explanation of one resolved node.", + "response": "raw/redux/redux-holdout-explain-primary.compass.0.stdout", + "sha256": "57be3b5fa8c5e038bdcabed6e39e1ae177223fc2801df76d902b86a086771e74" + }, + { + "judgment": "Text pass is an ambiguity list for library and test declarations, not a resolved explanation.", + "response": "raw/walkdir/walkdir-holdout-explain-primary.compass.0.stdout", + "sha256": "fa3577f28fdb9303d10f80f1e1107369975646a4315220857241acb002ade4e3" + }, + { + "judgment": "Text pass is an ambiguity list for library and test declarations, not a resolved explanation.", + "response": "raw/walkdir/walkdir-holdout-explain-target.compass.0.stdout", + "sha256": "06e2c99993e809ce65a0292c9f3c03f30ae19efd2a37da466deacea4bdf5534e" + }, + { + "judgment": "Text pass comes from candidate names. Response is needs_resolution and contains no direct callee answer.", + "response": "raw/walkdir/walkdir-holdout-callees.compass.0.stdout", + "sha256": "2187b9b854e9ccd9851bbc469e127dddfe79d0641340552d070871e69fd7522c" + }, + { + "judgment": "Text pass is a containment route between same-named test functions, not the registered library declarations or call.", + "response": "raw/walkdir/walkdir-holdout-path-forward.graphify.0.stdout", + "sha256": "2ffc33bac6667a18b7979e75b064ca2730fd19a7e93e977b28bc31382cea35f6" + }, + { + "judgment": "Text pass is a reversed containment route between same-named test functions, not the registered library declarations or call.", + "response": "raw/walkdir/walkdir-holdout-path-reverse.graphify.0.stdout", + "sha256": "736636244cca57cf06ce86a293a15b530c3c610b889eb1b14ff694458ae47ac7" + } + ], + "runDigest": "6412df38bbde56a1ec89974ac8d0a11162592bae90c403ed7b7f745acb6384bd", + "runId": "heldout-a-01", + "schema": "compass.heldout-panel-review/1", + "scope": "Post-output audit of the frozen, purposively selected confirmation panel A. Text matches do not establish source precision or successful resolution. No population-wide superiority claim. Later tuning is development.", + "sourcePathMatches": { + "compass": 6, + "graphify": 8 + }, + "sourcePathQuestionsPerTool": 10, + "textPasses": { + "compass": 46, + "graphify": 46 + }, + "textQuestionsPerTool": 55 +} diff --git a/benchmarks/agent_query/path_audit.py b/benchmarks/agent_query/path_audit.py index 6f0b1e952..86e6bc035 100644 --- a/benchmarks/agent_query/path_audit.py +++ b/benchmarks/agent_query/path_audit.py @@ -1,6 +1,6 @@ """Audit captured path responses against graphs and reviewed source witnesses. -This diagnoses the checked-in development cases. It does not estimate held-out +This checks explicitly reviewed source witnesses. It does not estimate population accuracy, accept endpoint echoes as paths, or choose among ambiguous labels. Run with ``python3 -m benchmarks.agent_query.path_audit --help``. """ @@ -144,6 +144,22 @@ def _edge_site(edge: dict, tool: str) -> tuple[object, object]: return edge.get("source_file"), int(match[1]) if match else None +def audit_observation(witness: dict, tool: str, graph: dict, output: str, + source_root: Path, observation: dict) -> dict: + """Keep unsuccessful requests in the denominator, even if they print a path.""" + result = audit_path(witness, tool, graph, output, source_root) + result["execution"] = {key: observation[key] for key in + ("exitCode", "timedOut", "followUps")} + if observation["exitCode"] != 0: + result["failures"].append(f"command exited {observation['exitCode']}") + if observation["timedOut"]: + result["failures"].append("command timed out") + if observation["followUps"]: + result["failures"].append("multiple responses are not supported by this path audit") + result["matched"] = not result["failures"] + return result + + def execute(args: argparse.Namespace) -> None: root = args.run.resolve() run = json.loads(read_bounded(root / "run.json")) @@ -180,12 +196,13 @@ def execute(args: argparse.Namespace) -> None: if hashlib.sha256(graph_bytes).hexdigest() != record[f"{tool}GraphSha256"]: raise ValueError("captured graph digest mismatch") observation = observations[repository, question, tool] - if observation["exitCode"] != 0 or observation["timedOut"] or observation["followUps"]: - raise ValueError("path audit requires a successful single-response execution") raw = read_bounded(root / "raw" / repository / f"{question}.{tool}.0.stdout") - if len(raw) != observation["stdoutBytes"]: + # stdoutBytes totals all pages; only compare it to the first capture + # for single-response requests. Unsupported follow-ups fail below. + if not observation["followUps"] and len(raw) != observation["stdoutBytes"]: raise ValueError("captured response length mismatch") - result = audit_path(witness, tool, json.loads(graph_bytes), raw.decode("utf-8"), source) + result = audit_observation(witness, tool, json.loads(graph_bytes), + raw.decode("utf-8"), source, observation) results.append({"repository": repository, "question": question, "stdoutSha256": hashlib.sha256(raw).hexdigest(), **result}) _verify_source(pinned, source) diff --git a/benchmarks/agent_query/tests/test_path_audit.py b/benchmarks/agent_query/tests/test_path_audit.py index 48e6a2eed..e143b47dd 100644 --- a/benchmarks/agent_query/tests/test_path_audit.py +++ b/benchmarks/agent_query/tests/test_path_audit.py @@ -5,7 +5,7 @@ import tempfile import unittest -from benchmarks.agent_query.path_audit import audit_path, parse_path +from benchmarks.agent_query.path_audit import audit_observation, audit_path, parse_path class PathAuditTests(unittest.TestCase): @@ -122,6 +122,28 @@ def test_hop_count_is_bounded(self) -> None: with self.assertRaises(ValueError): parse_path("Shortest path (1000000 hops):\n start()\n") + def test_unsuccessful_execution_never_passes_even_with_a_valid_path(self) -> None: + successful = {"exitCode": 0, "timedOut": False, "followUps": 0} + for output in ("", self.output): + for field, value, reason in (("exitCode", 1, "command exited"), + ("timedOut", True, "timed out"), + ("followUps", 1, "multiple responses")): + observation = {**successful, field: value} + with self.subTest(field=field, output=output): + result = audit_observation(self.witness, "compass", self.compass, + output, self.root, observation) + self.assertFalse(result["matched"]) + self.assertEqual(result["execution"], observation) + self.assertTrue(any(reason in failure for failure in result["failures"])) + self.assertTrue(audit_observation(self.witness, "compass", self.compass, + self.output, self.root, successful)["matched"]) + + def test_source_drift_still_aborts_an_unsuccessful_execution(self) -> None: + (self.root / "code.rs").write_text("changed\n") + with self.assertRaisesRegex(ValueError, "source witness changed"): + audit_observation(self.witness, "compass", self.compass, "", self.root, + {"exitCode": 1, "timedOut": False, "followUps": 0}) + if __name__ == "__main__": unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 1777afee5..bf80c1a41 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -997,6 +997,112 @@ checkpoint: no extractor, publication, or viewer format changes are made here. God-object detection, community cohesion, source-edge precision, and explanation usefulness on held-out tasks remain open. +## Frozen confirmation panel A: competitor advantages remain + +Inputs were committed as `7c70fbea` before extraction or queries: 55 questions, +21 selected relationship pairs, and ten forward/reverse path witnesses. +Chi (Go), Click (Python), jsoup (Java), Redux (TypeScript), and WalkDir (Rust) +are new repositories for this checkpoint. Both tools use the same five clean, +pinned checkouts. Selection is purposive, not representative. The Compass +executable is the frozen `5382b51b…` hub-evidence build above; Graphify reports +0.9.67. `heldout-a-01` completed all ten builds and 110 question workflows. +Source commits, registered input bytes, captured graph digests, runner digest, +and capture byte totals were rechecked. The CLI records executable hashes; +Graphify's launcher hash does **not** attest to all Python package/dependency +bytes during execution. Do not conflate the separate MCP environment check +with a pre/post CLI package check. + +### Original text-recall scores + +| Surface | Compass | Graphify | +| --- | ---: | ---: | +| Explain | 10/10 | 8/10 | +| Direct callers plus incoming-call ask | 8/10 | 9/10 | +| Callees | 4/5 | 4/5 | +| Symbol paths, both directions | 6/10 | 10/10 | +| File paths | 5/5 | 1/5 | +| Ambiguity | 5/5 | 4/5 | +| Missing symbols | 5/5 | 5/5 | +| Broad natural query | 3/5 | 5/5 | +| Total text matches | 46/55 | 46/55 | + +There are 38 shared passes, eight exclusive passes per tool, and one shared +failure. On shared text passes, median estimated answer tokens are 158 Compass +versus 104 Graphify. The single-run overall wall-time medians are 738 versus +236 ms; these are observations, not a controlled performance benchmark. +Graphify sometimes exceeds the requested text budget (for example the Chi ask +response is 2,744 estimated tokens against a requested 2,000). All consumed +responses and documented follow-ups remain in the cost totals. + +These are **not correctness scores**. Manual response inspection catches four +Compass text passes that do not resolve the requested operation: Redux's +primary explanation, both WalkDir explanations, and WalkDir callees merely +list ambiguity candidates. The registered text criteria find names/lines in +those lists. Graphify's two WalkDir symbol paths also pass by printing the +requested words while selecting same-named **test** functions and walking +through their containing test file. Its stderr warns of ambiguity; the body +still provides a different route. The frozen run is preserved, with these +judgments recorded separately in `heldout_panel_a_review.json`. + +For incoming-call ask, Compass prints the requested callee and incoming +relationships on Chi, Click, jsoup, and Redux; WalkDir remains unresolved. +Compass's usage results include references/imports where explicitly labeled, +not just calls. Graphify's query answers return broader neighborhoods that +contain the selected incoming call; WalkDir combines library and test seeds. +These inspected facts do not establish the precision of every returned edge. + +### Source and identity checks + +The path auditor now retains failed commands as failed rows rather than +aborting and dropping the remaining denominator. Nonzero exit, timeout, and +unsupported multi-response execution cannot pass even if stdout prints a valid +path. Source drift and mismatched graph provenance still abort the audit. +All ten witnesses remain in each tool's denominator. Source-verified paths +are **6/10 Compass versus 8/10 Graphify**: both pass Chi, Click, and jsoup; +Graphify also passes Redux; neither establishes the requested WalkDir route. +Compass refuses WalkDir's genuinely ambiguous unqualified names and Redux's +export/function name collision. An ambiguity refusal is safer than selecting +a wrong declaration, but it is still not a completed path task. + +The initial edge audit also exposed an **oracle mistake**. The registered +Click witness omitted `open_stream`'s second call to `_wrap_io_open` at +`src/click/_compat.py:450`. Compass preserved both calls, while Graphify kept +only line 397. The original witness and audit remain unchanged. A separately +named `edge_witnesses_heldout_click_corrected.json` adds line 450 after reviewing +the entire function; it is an explicitly post-output diagnostic correction. + +| Selected-pair evidence | Compass | Graphify | +| --- | ---: | ---: | +| Unique endpoint identity, original or corrected | 17/21 | 21/21 | +| Relationship/absence matches, original or corrected | 15/21 | 20/21 | +| Full occurrence agreement, registered witness | 14/21 | 18/21 | +| Reviewed positive occurrences, registered witness | 13/18 | 15/18 | +| Full occurrence agreement, corrected diagnostic | 15/21 | 17/21 | +| Reviewed positive occurrences, corrected diagnostic | 14/19 | 15/19 | + +The 21 pairs comprise 16 positive pairs and five direct-edge negatives. +Negatives pass only with both endpoints uniquely identified. Four Compass +Redux pairs fail identity checks because separate export and function nodes +share the exact file/start-line/terminal name; they must not be described as +four missing call edges. The plain-symbol ambiguity is a real workflow issue, +while the source audit cannot choose between those nodes using its registered +identity rule. Compass also misses Chi's source-proven `rctx.URLParam` call. +Both tools miss jsoup's chained `new Cleaner(...).isValidBodyHtml(...)` call. +Graphify loses repeated occurrences in jsoup and WalkDir as well as corrected +Click. This checks selected pairs, not complete callee sets or graph precision. + +Artifacts are `heldout-a-path-audit-01.json`, five +`heldout-a-*-edge-audit-01.json` files, and +`heldout-a-click-edge-audit-corrected-01.json` under the evaluation root. +The checked-in review records their digests, input digests, response-review +digests, and limits. Auditor regressions pass all 95 Python tests, including +unsuccessful execution and source-drift cases (`heldout-a-auditor-tests-01.log`). +This panel contradicts a broad superiority claim. Its first run remains a +confirmation checkpoint; subsequent product tuning on it is development and +requires another independent confirmation panel. MCP/community workflows, +directed or long walks, source-level cohesion, and god-object judgments remain +unproven here. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact From 85caa5494d83d10410c8c522855f71c938bfa3c8 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 18:21:18 -0700 Subject: [PATCH 24/97] fix: make ambiguous query answers explicit and actionable --- CHANGELOG.md | 4 + COMPATIBILITY.md | 6 + .../heldout_panel_a_path_diagnostics.json | 170 ++++++++++++++++++ crates/compass-cli/tests/code_query_cli.rs | 30 ++++ crates/compass-output/src/agent_query.rs | 25 ++- crates/compass-output/tests/agent_query.rs | 52 ++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 66 +++++++ docs/reference/outputs.md | 7 + 8 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 benchmarks/agent_query/heldout_panel_a_path_diagnostics.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5177943..f8d91af1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Make ambiguous typed query headlines request exact node IDs without naming + a fallback subject or claiming no path. Include IDs for all retained + ambiguity candidates in text output. + - Explain MCP hub candidates with node kind and bounded relation/direction counts, preserving parallel records and distinguishing incident records from ranking degree. Report omitted relation categories explicitly. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index c599e0484..3d0afff6e 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -731,6 +731,12 @@ leading dots, and trailing empty parentheses), retaining exact node-ID lookup and requiring a unique normalized name. Existing schema majors and raw query responses are unchanged. Text cursors whose primary ordering changed are rejected by the existing prefix check; reissue the question. +Ambiguous typed answers now use the operation as their answer basis and ask +for exact node IDs, with IDs printed for every retained ambiguity candidate. +They no longer describe the first candidate as a fallback answer or imply +that an ambiguous path request proved disconnection. Schema majors and raw +query responses are unchanged. Existing text cursor prefix checks reject +pages whose candidate rendering changed; restart that query from page one. Plain `compass query` against a typed graph now defaults to `compass.query.discovery/1`; `--dfs` and `--context` compose with discovery. Explicit `--traverse` or legacy-only `--budget`/`--page` diff --git a/benchmarks/agent_query/heldout_panel_a_path_diagnostics.json b/benchmarks/agent_query/heldout_panel_a_path_diagnostics.json new file mode 100644 index 000000000..0edd4e3d7 --- /dev/null +++ b/benchmarks/agent_query/heldout_panel_a_path_diagnostics.json @@ -0,0 +1,170 @@ +{ + "schema": "compass.heldout-path-diagnostics/1", + "scope": "Post-output diagnostics on the original frozen binaries and graphs. Prepared endpoints do not measure node retrieval or establish an end-to-end workflow cost advantage. Neither arm replaces the original confirmation score. Graphify path help does not promise exact-ID semantics.", + "arms": [ + { + "arm": "stored_ids", + "artifact": "heldout-a-explicit-path-diagnostic-01", + "auditDigest": "a955696961842c00576155bb8d6cc1ed970f6667e974a30e45933f9b913318f9", + "inputDigest": "d5bdd613377644feac078827f7cc5a5b43ce36309579be721b3e706a0933feb3", + "results": [ + { + "repository": "redux", + "question": "redux-holdout-path-forward", + "tool": "compass", + "matched": true, + "failures": [], + "stdoutSha256": "a2d25eba2c1848d27c99659d3c66c690a14f57eb95872097ca2612ec123d3a0b" + }, + { + "repository": "redux", + "question": "redux-holdout-path-forward", + "tool": "graphify", + "matched": true, + "failures": [], + "stdoutSha256": "317bc379d4a10fe6b621471754b2619265f392bfa85f4343075925022dc4dbc6" + }, + { + "repository": "redux", + "question": "redux-holdout-path-reverse", + "tool": "compass", + "matched": true, + "failures": [], + "stdoutSha256": "114e4cd102e00074a384f50f792981bdeb6578f6dedc24d85a4bbf9799ad8930" + }, + { + "repository": "redux", + "question": "redux-holdout-path-reverse", + "tool": "graphify", + "matched": true, + "failures": [], + "stdoutSha256": "74baa786104a9342fa52e86a0ca550920bb0f29a380237c0b40ebf1ee25e2c6d" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-forward", + "tool": "compass", + "matched": true, + "failures": [], + "stdoutSha256": "fa9d7fd0d98551e1bf1c73dd520f9cea07d6c5205e8052ec7aa617a9427568e2" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-forward", + "tool": "graphify", + "matched": false, + "failures": [ + "route length differs from reviewed witness" + ], + "stdoutSha256": "dceff48f7dcf0eb1aebcd94c482f629e7ad35c1095f395a693cca52bc532cea5" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-reverse", + "tool": "compass", + "matched": true, + "failures": [], + "stdoutSha256": "a4a1b0b112b9d1761590ae71752d69b4b1f3807e04097ea74054a9ba53a27043" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-reverse", + "tool": "graphify", + "matched": false, + "failures": [ + "route length differs from reviewed witness" + ], + "stdoutSha256": "1daec3b41504b36036dbf6a8338a05d9d4d53a837a863ead7302f96f042baeee" + } + ] + }, + { + "arm": "display_labels", + "artifact": "heldout-a-label-path-diagnostic-01", + "auditDigest": "33197715aa55be793ae3612af827dd7cb2d1729a3bc31b81c0a2753e4d918628", + "inputDigest": "6f95aefaf813919be5baef2e4ae97c91e5bc7eafd839eecd65c4c1b3803e65f6", + "results": [ + { + "repository": "redux", + "question": "redux-holdout-path-forward", + "tool": "compass", + "matched": false, + "failures": [ + "expected exactly one rendered path header", + "command exited 1" + ], + "stdoutSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "repository": "redux", + "question": "redux-holdout-path-forward", + "tool": "graphify", + "matched": true, + "failures": [], + "stdoutSha256": "317bc379d4a10fe6b621471754b2619265f392bfa85f4343075925022dc4dbc6" + }, + { + "repository": "redux", + "question": "redux-holdout-path-reverse", + "tool": "compass", + "matched": false, + "failures": [ + "expected exactly one rendered path header", + "command exited 1" + ], + "stdoutSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "repository": "redux", + "question": "redux-holdout-path-reverse", + "tool": "graphify", + "matched": true, + "failures": [], + "stdoutSha256": "74baa786104a9342fa52e86a0ca550920bb0f29a380237c0b40ebf1ee25e2c6d" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-forward", + "tool": "compass", + "matched": false, + "failures": [ + "expected exactly one rendered path header", + "command exited 1" + ], + "stdoutSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-forward", + "tool": "graphify", + "matched": false, + "failures": [ + "route length differs from reviewed witness" + ], + "stdoutSha256": "2ffc33bac6667a18b7979e75b064ca2730fd19a7e93e977b28bc31382cea35f6" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-reverse", + "tool": "compass", + "matched": false, + "failures": [ + "expected exactly one rendered path header", + "command exited 1" + ], + "stdoutSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "repository": "walkdir", + "question": "walkdir-holdout-path-reverse", + "tool": "graphify", + "matched": false, + "failures": [ + "route length differs from reviewed witness" + ], + "stdoutSha256": "736636244cca57cf06ce86a293a15b530c3c610b889eb1b14ff694458ae47ac7" + } + ] + } + ] +} diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index b991a9e0e..8a26a7126 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -1639,6 +1639,13 @@ fn ambiguous_typed_lookup_returns_a_pick_list_instead_of_an_empty_result() let view: Value = serde_json::from_str(&outcome.stdout)?; assert_eq!(view["status"]["resultState"], "needs_resolution"); assert_eq!(view["status"]["matchState"], "ambiguous"); + assert!( + view["answer"]["headline"] + .as_str() + .is_some_and(|headline| headline.contains("multiple candidates") + && headline.contains("exact node ID")) + ); + assert_eq!(view["answer"]["basis"][0]["kind"], "operation"); let results = view["primaryResults"] .as_array() .ok_or("primaryResults must be an array")?; @@ -1655,6 +1662,29 @@ fn ambiguous_typed_lookup_returns_a_pick_list_instead_of_an_empty_result() "{}", outcome.stdout ); + for argv in [ + vec!["callers", "run"], + vec!["callees", "run"], + vec!["ask", "who calls run"], + ] { + let mut args = argv.into_iter().map(OsString::from).collect::>(); + args.extend([OsString::from("--graph"), graph.as_os_str().to_owned()]); + let text = run(Frontend::Compass, args); + assert_eq!(text.code, 0, "{}", text.stderr); + assert!( + text.stdout.contains("multiple candidates"), + "{}", + text.stdout + ); + assert!(text.stdout.contains("id: n:alpha-run"), "{}", text.stdout); + assert!(text.stdout.contains("id: n:beta-run"), "{}", text.stdout); + assert!( + !text.stdout.contains("fallback candidate"), + "{}", + text.stdout + ); + assert!(!text.stdout.contains("No exact match"), "{}", text.stdout); + } Ok(()) } diff --git a/crates/compass-output/src/agent_query.rs b/crates/compass-output/src/agent_query.rs index 93226eed2..d1fdff41a 100644 --- a/crates/compass-output/src/agent_query.rs +++ b/crates/compass-output/src/agent_query.rs @@ -1963,20 +1963,18 @@ fn duplicated_entity_labels(entities: &[AgentEntity]) -> HashSet<&str> { /// Whether a page must print the stable identifier of one entity. /// -/// A page needs the identifier only where the page is resolving a name and the -/// name it printed is not unique enough to address the row, which is when two -/// retained rows share that label. A row that a caller can name - the resolved -/// answers, and every distinct label in a candidate list - stays addressed by -/// the qualified name and source anchor it already prints, which keeps the -/// answer's evidence per token high; a duplicated label cannot be repeated back -/// to the tool alone, so those rows carry the identity that separates them. The -/// candidate list, its order, and every identifier stay in `--format json`. +/// An ambiguous query asks the user to select an exact ID, so every retained +/// candidate carries that ID even when its qualified label is distinct. Other +/// unresolved candidate lists need IDs only for colliding labels. Resolved +/// answers stay compact; JSON carries every identifier unchanged. fn entity_needs_identity( entity: &AgentEntity, match_state: AgentMatch, duplicated_labels: &HashSet<&str>, ) -> bool { - !matches!(match_state, AgentMatch::Exact) && duplicated_labels.contains(entity.label.as_str()) + match_state == AgentMatch::Ambiguous + || (!matches!(match_state, AgentMatch::Exact) + && duplicated_labels.contains(entity.label.as_str())) } fn render_relationship(relationship: &AgentRelationship) -> String { @@ -2683,6 +2681,15 @@ fn answer_for_code( .first() .map(|operand| operand.value.clone()) .unwrap_or_else(|| "the requested query".to_owned()); + if result_state == AgentResultState::NeedsResolution { + return AgentAnswer { + headline: "The query matches multiple candidates; retry with an exact node ID for each ambiguous operand.".to_owned(), + basis: vec![AgentBasis { + kind: "operation".to_owned(), + id: context.operation.label().to_owned(), + }], + }; + } let subject = primary_results .first() .map(|entity| entity.label.clone()) diff --git a/crates/compass-output/tests/agent_query.rs b/crates/compass-output/tests/agent_query.rs index b797f2152..69e2351e4 100644 --- a/crates/compass-output/tests/agent_query.rs +++ b/crates/compass-output/tests/agent_query.rs @@ -853,6 +853,58 @@ fn legacy_page_cursor_encoding_is_rejected_with_a_version_error() -> Result<(), Ok(()) } +#[test] +fn ambiguity_never_selects_a_headline_subject_or_claims_no_path() -> Result<(), Box> { + for (operation, agent_operation) in [ + (CodeQueryOperation::Callers, AgentOperation::Callers), + (CodeQueryOperation::Callees, AgentOperation::Callees), + (CodeQueryOperation::Impact, AgentOperation::Impact), + (CodeQueryOperation::NodeTrail, AgentOperation::NodeTrail), + ] { + let mut response = response(operation); + response.nodes = vec![ + node("n:library", "Library.run", &anchor("src/lib.rs", 10)), + node("n:test", "Tests.run", &anchor("tests/run.rs", 20)), + ]; + response.diagnostics.push(QueryDiagnostic { + code: QueryDiagnosticCode::AmbiguousMatch, + message: "run matched two declarations".to_owned(), + node_id: None, + path: None, + }); + let query_context = + context(agent_operation).with_operand(compass_output::AgentOperandRole::Symbol, "run"); + for reverse in [false, true] { + if reverse { + response.nodes.reverse(); + } + let view = build_code_query_view(&response, query_context.clone())?; + assert_eq!(view.status.result_state, AgentResultState::NeedsResolution); + assert!(view.answer.headline.contains("multiple candidates")); + assert!(view.answer.headline.contains("exact node ID")); + assert_eq!(view.answer.basis.len(), 1); + assert_eq!(view.answer.basis[0].kind, "operation"); + let page = render_code_query_text_page( + &response, + query_context.clone(), + AgentTextPageOptions { + token_budget: 2_000, + cursor: None, + }, + )?; + for text in [render_agent_query_text(&view)?, page.text] { + for id in ["n:library", "n:test"] { + assert!(text.contains(&format!("id: {id}")), "{text}"); + } + assert!(!text.contains("fallback candidate"), "{text}"); + assert!(!text.contains("No directed path"), "{text}"); + assert!(!text.contains("No exact match"), "{text}"); + } + } + } + Ok(()) +} + #[test] fn unresolved_relationship_answers_never_speak_for_another_symbol() -> Result<(), Box> { let caller_anchor = anchor("src/caller.rs", 10); diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index bf80c1a41..5b57806d4 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1103,6 +1103,72 @@ requires another independent confirmation panel. MCP/community workflows, directed or long walks, source-level cohesion, and god-object judgments remain unproven here. +### Post-output path selection diagnostics + +Two additional arms use the **original frozen binaries and graphs**, with +source-reviewed callable endpoints prepared symmetrically from the graphs. +They are post-output diagnostics, not node retrieval or workflow-cost scores: +preparing an endpoint from the oracle does not prove that a user found it. +The four questions cover forward/reverse Redux and WalkDir paths only. + +| Prepared endpoint form | Compass source-path matches | Graphify source-path matches | +| --- | ---: | ---: | +| Full stored IDs | 4/4 | 2/4 | +| Exact stored display labels | 0/4 | 2/4 | + +Both graphs contain the selected WalkDir call edges. Full IDs let Compass +navigate those edges and Redux's function declarations. Graphify's CLI path +help describes source/target strings without promising exact-ID semantics; +its stored-ID and display-label attempts still choose different WalkDir +endpoints. For example the full-ID forward request returns a test function, +its containing test file, and an import to `WalkDir`. Compass normalizes display +labels and still detects the export/function or library/test collisions. +Neither arm replaces the original 6/10 versus 8/10 source-path result, and the +ID diagnostic is not a claim that Graphify lacks other disambiguation workflows. +Artifacts: `heldout-a-explicit-path-diagnostic-01` and +`heldout-a-label-path-diagnostic-01`, each retaining prepared inputs, executable +hashes, script, captured streams, and source-witness audit results. + +### Development correction: actionable ambiguity answers + +The frozen panel exposed an unrelated presentation defect: a typed ambiguous +relationship request said “No exact match” and named the first candidate as a +fallback. A typed ambiguous node-trail response could instead claim no directed +path. `compass-output` now handles `needs_resolution` before those answer +branches, asks for exact IDs, and uses the operation as the answer basis. +Every retained ambiguity candidate has its exact ID in paged and full text, +even if qualified labels differ. Candidate selection, raw query results, +relationship resolution, schema majors, and path algorithms are unchanged. +Changed text pages use the existing cursor-prefix rejection rules; restart a +rejected continuation from page one. + +This is **development after observing panel A**, not an improved held-out +score. `heldout-a-ambiguity-replay-01` repeats all 55 Compass questions against +the original hash-verified graphs. Text passes remain **46/55**, with no changed +pass/fail rows. All four inspected Redux/WalkDir ambiguous callers/callees/ask +responses now show exact IDs and the corrected headline. The text-oracle false +positives documented above remain false positives; better ambiguity wording +does not turn an unresolved task into a successful answer. The replay does not +re-extract either graph or rerun Graphify. + +The new frozen binary has SHA256 +`d561d7c762410899cb6039f409d17401239ee26ee525f8bbfd2e95cebcb82857`. +`ambiguity-headline-provenance` records its base commit, patch, and source hashes. +Verification passes **1,155 native tests, zero failed, two ignored**, comprising +workspace library/binary tests plus `agent_query`, `code_query_cli`, and +`compass_product`. The new regressions cover callers, callees, impact, node +trails, candidate permutation, full/paged text, agent JSON, and the actual CLI +callers/callees/ask boundary. Workspace and the same selected integration Clippy +pass with warnings denied. All **95 Python tests**, formatting, diff checks, +and product-boundary checks pass. Logs are `ambiguity-headline-native-02.log`, +`ambiguity-headline-clippy-01.log`, and `ambiguity-headline-python-01.log`. +The first targeted test compile used a nonexistent test-options default; the +retained `ambiguity-headline-targeted-01.log` records that development error. +Explicit options corrected it before the full pass. Existing core unused-mut +and macOS linker warnings remain in build/test logs. Extraction qualification +and JavaScript gates were not rerun: this change only projects query ambiguity +and does not change extraction, publication, or viewer assets. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 06a898804..f9b5a0a23 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -941,6 +941,13 @@ anchors, reports `status.matchState = ambiguous`, and emits instead of issuing a broad search. Primary results are deduplicated by node ID, including when a real self-edge names the same node twice. +Ambiguous typed answers explicitly ask for an exact node ID and use the query +operation as their answer basis. They do not attribute an answer to the first +candidate, claim the symbol is missing, or infer that a path does not exist. +Text pages include IDs for every retained ambiguity candidate, even when their +qualified labels differ. Select the intended declaration by its source anchor +and retry with its ID. + Typed text output is paged. Each page carries a `Pagination: page=N range=A-B of T next=` footer; `--cursor` continues the same ledger at the same `--text-budget`. The cursor is a checksummed From a5f4fe88d5115d22e99771964878499d6c4cd512 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 18:53:17 -0700 Subject: [PATCH 25/97] fix: resolve Go control receivers without leaking shadowed types --- CHANGELOG.md | 6 + COMPATIBILITY.md | 13 + MIGRATION.md | 7 +- benchmarks/agent_query/README.md | 8 + ..._witnesses_go_receiver_chi_diagnostic.json | 127 ++++ .../go_receiver_development_review.json | 541 ++++++++++++++++++ crates/compass-files/src/cache.rs | 2 +- crates/compass-files/tests/contracts.rs | 6 + .../compass-languages/src/evidence/build.rs | 270 +++++---- .../tests/universal_evidence.rs | 82 +++ .../tests/universal_resolution/go.rs | 144 +++++ ...ode-graph-intelligence-audit-2026-09-26.md | 71 +++ docs/reference/universal-semantic-evidence.md | 13 + 13 files changed, 1163 insertions(+), 127 deletions(-) create mode 100644 benchmarks/agent_query/edge_witnesses_go_receiver_chi_diagnostic.json create mode 100644 benchmarks/agent_query/go_receiver_development_review.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d91af1d..ba7ea1420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Resolve Go receivers introduced by `if` and switch initializers using their + source-proven factory results. Respect nearer locals, range bindings, and + closure parameters instead of borrowing an outer receiver type; do not + substitute a same-named package factory for a callback or attribute a returned + callback invocation to its factory's receiver type. Rebuild older AST caches. + - Make ambiguous typed query headlines request exact node IDs without naming a fallback subject or claiming no path. Include IDs for all retained ambiguity candidates in text output. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 3d0afff6e..b91edb987 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -206,6 +206,19 @@ type is proven. The advertised producer capabilities and evidence/graph schemas are unchanged. AST cache semantics advance from 2 to 3, rebuilding prior AST facts automatically across languages. Published historical graphs are unchanged. +Go receiver lookup now includes `if` and switch initializers alongside loop +initializers. Block locals, range variables, and closure parameters are resolved +in lexical order; an unsupported nearer binding cannot inherit an outer +parameter's receiver type. Local callbacks and shadowed package names cannot +provide a same-named global factory's return type. Type-switch aliases block +outer types but do not yet infer case-specific narrowing. Invoking a returned +callback retains the inner factory call without treating the outer invocation +as a reference to the factory receiver's type. Newly recovered and +corrected call edges require rebuilding the graph. AST cache semantics advance +from 3 to 4, invalidating older disposable AST facts across languages. Producer +capabilities and evidence/graph schemas are unchanged; published history remains +immutable. + ### Agent Query View Compass adds the additive strict projection `compass.query.agent-view/1` for diff --git a/MIGRATION.md b/MIGRATION.md index d583b7b75..e401c1629 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -5,7 +5,12 @@ sidecars. Its output root now preserves the familiar flat artifact shape so file-based workflows can transition while Compass's snapshot and store layout remains visible and clearly owned. -## Query text and path resolution +## Graph rebuilds and query resolution + +Rebuild existing Go graphs to receive the control-initializer and receiver +shadowing corrections. Normal builds automatically discard AST cache versions +older than 4. Source-proven calls can appear and incorrectly attributed calls +can disappear; existing historical realizations are not rewritten. For MCP `shortest_path`, replace partial keywords with exact IDs or complete symbol/qualified names. Handle ambiguity candidates before retrying. Use diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 38eccfdff..7295d9238 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -243,6 +243,14 @@ the panel. The question score remains a text-recall proxy; report independent identity/direction/occurrence checks and all failures separately. Repository selection is purposive, so this does not estimate population accuracy. +The Go receiver development follow-up is recorded separately in +`go_receiver_development_review.json`. Its complete relationship-delta review +retains the intermediate Cobra callback regression. The additional Chi +compression-interface witness lives in +`edge_witnesses_go_receiver_chi_diagnostic.json`; it is a post-output diagnostic, +not an addition to the original held-out score. Run it with the same +`edge_audit --run ... --witnesses ... --output ...` interface. + The first frozen results and post-output review are recorded in `heldout_panel_a_review.json` and the main code-graph intelligence audit report. Keep the original Click edge witness: its missing second `_wrap_io_open` site diff --git a/benchmarks/agent_query/edge_witnesses_go_receiver_chi_diagnostic.json b/benchmarks/agent_query/edge_witnesses_go_receiver_chi_diagnostic.json new file mode 100644 index 000000000..2d0466fed --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_go_receiver_chi_diagnostic.json @@ -0,0 +1,127 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "scope": "Post-output development diagnostic after panel A. Four unchanged registered pairs plus the source-reviewed compression interface call found by inspecting the full graph delta. Not independent confirmation or whole-graph precision.", + "witnesses": [ + { + "id": "chi-source-edge-1", + "source": { + "file": "chi.go", + "line": 62, + "symbol": "NewRouter", + "text": "func NewRouter() *Mux {" + }, + "target": { + "file": "mux.go", + "line": 52, + "symbol": "NewMux", + "text": "func NewMux() *Mux {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "chi.go", + "line": 63, + "text": "return NewMux()" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-2", + "source": { + "file": "context.go", + "line": 16, + "symbol": "URLParamFromCtx", + "text": "func URLParamFromCtx(ctx context.Context, key string) string {" + }, + "target": { + "file": "context.go", + "line": 25, + "symbol": "RouteContext", + "text": "func RouteContext(ctx context.Context) *Context {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "context.go", + "line": 17, + "text": "if rctx := RouteContext(ctx); rctx != nil {" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-3", + "source": { + "file": "context.go", + "line": 16, + "symbol": "URLParamFromCtx", + "text": "func URLParamFromCtx(ctx context.Context, key string) string {" + }, + "target": { + "file": "context.go", + "line": 124, + "symbol": "URLParam", + "text": "func (x *Context) URLParam(key string) string {" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "context.go", + "line": 18, + "text": "return rctx.URLParam(key)" + } + ], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-source-edge-4", + "source": { + "file": "chi.go", + "line": 62, + "symbol": "NewRouter", + "text": "func NewRouter() *Mux {" + }, + "target": { + "file": "context.go", + "line": 31, + "symbol": "NewRouteContext", + "text": "func NewRouteContext() *Context {" + }, + "relation": "calls", + "expected": "absent", + "occurrences": [], + "judgment": "All direct call sites for this selected declaration pair in the reviewed caller body are listed; an empty list is a direct-edge negative, not a claim of no transitive connection." + }, + { + "id": "chi-compression-interface-diagnostic", + "source": { + "file": "middleware/compress.go", + "line": 357, + "symbol": "Flush", + "text": "func (cw *compressResponseWriter) Flush() {" + }, + "target": { + "file": "middleware/compress.go", + "line": 354, + "symbol": "Flush", + "text": "Flush() error" + }, + "relation": "calls", + "expected": "present", + "occurrences": [ + { + "file": "middleware/compress.go", + "line": 364, + "text": "f.Flush()" + } + ], + "judgment": "The comma-ok type assertion to compressFlusher at line 363 supports this interface-method call. This is not a claim about the concrete runtime implementation. Other f bindings at lines 358 and 367 assert the distinct http.Flusher type." + } + ] +} diff --git a/benchmarks/agent_query/go_receiver_development_review.json b/benchmarks/agent_query/go_receiver_development_review.json new file mode 100644 index 000000000..60538dd8e --- /dev/null +++ b/benchmarks/agent_query/go_receiver_development_review.json @@ -0,0 +1,541 @@ +{ + "artifactDigests": { + "go-callback-regression-after-01.log": "8f49025c2134d0a0b880b93d054fd27af10b56d0268dc16243685eeae0760d57", + "go-callback-regression-before-01.log": "4ecb63cc44c9397e0273a2f3fdc2ab1d14151d3bd16cd795938d52d91056cab2", + "go-initializer-chi-diagnostic-edge-audit-01.json": "35b94fe0687072106320ebfde1b4b712fd891742ddb5f1a7b0bc6eb2d93eb79c", + "go-initializer-chi-diagnostic-edge-audit-02.json": "2f8963363b492572d9af86929786ef8391d35a388d6fd170804021a77a111af6", + "go-initializer-clippy-02.log": "d423b7f39f17cadab6fc174d5e9b69412b0dec968efb6d2a8847f8a7d9045911", + "go-initializer-final-delta-01.json": "9ee59cbc29c017e28a1d2f3cd8524afdf5fe004d8756ac179535d3a8c2d782c2", + "go-initializer-native-03.log": "dcb437d6aa7fdadd3e7377d481532181a6634ecbc990b8940210e9cca468e360", + "go-initializer-python-02.log": "c8d67e5826b21f7eef14a1c4f2199f6031e77a8ddb61861be33cf790942a5037", + "go_initializer_delta.py": "6f1642fafa950051375382d86615b620c23e582cf9d8b70c8a8e33197e54d164" + }, + "diagnosticResult": { + "compressionInterfacePair": { + "compass": true, + "graphify": false + }, + "registeredFourPairs": { + "compass": 4, + "graphify": 4 + }, + "scope": "Post-output diagnostic; exact unique endpoints on both tools; Graphify lacks this selected call. Not a population precision/recall score." + }, + "finalBinarySha256": "bb09a5335c80fd7dd710e008b4a2f30f9403108e4883ed74e7d7c0de1b0aaca9", + "finalComparisons": [ + { + "captures": { + "baseline": { + "calls": 916, + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "graphDigests": { + "compass": "e7a5dcb40973eadb8281416e125ef0b78a6330b74e0361ac52dcb083ba77204a", + "graphify": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + "nodes": 729, + "questionsPerTool": 11, + "relationships": 2000, + "run": "heldout-a-01", + "runSha256": "6412df38bbde56a1ec89974ac8d0a11162592bae90c403ed7b7f745acb6384bd", + "textPasses": { + "compass": 11, + "graphify": 10 + } + }, + "final": { + "calls": 918, + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "graphDigests": { + "compass": "13a565a61fea5cf259e2e898e0f62a5d48e6d491f60f1ea862f1560065fdd16e", + "graphify": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + "nodes": 729, + "questionsPerTool": 11, + "relationships": 2002, + "run": "go-initializer-chi-02", + "runSha256": "d1e04cc7bdf0e09588ec9a3fbda96ee67102387358cb18ae5d974486f46f416c", + "textPasses": { + "compass": 11, + "graphify": 10 + } + }, + "intermediate": { + "calls": 918, + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "graphDigests": { + "compass": "13a565a61fea5cf259e2e898e0f62a5d48e6d491f60f1ea862f1560065fdd16e", + "graphify": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + "nodes": 729, + "questionsPerTool": 11, + "relationships": 2002, + "run": "go-initializer-chi-01", + "runSha256": "6aca80be34b6a893052302e20ce31ef11b4b88b272712e25e6b8ccea76efeb36", + "textPasses": { + "compass": 11, + "graphify": 10 + } + } + }, + "finalDeltaFrom": { + "baseline": [ + { + "multiplicity": 1, + "relation": "calls", + "site": { + "endByte": 436, + "endColumn": 22, + "endLine": 18, + "file": "context.go", + "startByte": 423, + "startColumn": 9, + "startLine": 18 + }, + "siteText": "rctx.URLParam", + "source": [ + "chi.URLParamFromCtx", + "context.go", + 16 + ], + "sourceLine": "\t\treturn rctx.URLParam(key)", + "sourceSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "status": "added", + "target": [ + "chi.Context::URLParam", + "context.go", + 124 + ] + }, + { + "multiplicity": 1, + "relation": "calls", + "site": { + "endByte": 10958, + "endColumn": 9, + "endLine": 364, + "file": "middleware/compress.go", + "startByte": 10951, + "startColumn": 2, + "startLine": 364 + }, + "siteText": "f.Flush", + "source": [ + "middleware.compressResponseWriter::Flush", + "middleware/compress.go", + 357 + ], + "sourceLine": "\t\tf.Flush()", + "sourceSha256": "1a54e1cd05a913dd26bc00faab499cddc58a00646a92af5051961100d1532698", + "status": "added", + "target": [ + "middleware.compressFlusher::Flush", + "middleware/compress.go", + 354 + ] + } + ], + "intermediate": [] + }, + "repository": "chi" + }, + { + "captures": { + "baseline": { + "calls": 2101, + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "graphDigests": { + "compass": "6f862024bd68b88b734a43421a21cc2074a94ab7d68cc8857d59609112c9c7db", + "graphify": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + "nodes": 656, + "questionsPerTool": 10, + "relationships": 3369, + "run": "v2-shadow-05", + "runSha256": "1b05861d3f1b2d2deaba0b13cb3b9712ded14afa69a52fe1ca61df6cda2b67b6", + "textPasses": { + "compass": 10, + "graphify": 9 + } + }, + "final": { + "calls": 2101, + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "graphDigests": { + "compass": "6f862024bd68b88b734a43421a21cc2074a94ab7d68cc8857d59609112c9c7db", + "graphify": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + "nodes": 656, + "questionsPerTool": 10, + "relationships": 3369, + "run": "go-initializer-cobra-02", + "runSha256": "92604fa3ef8bc964a9e581e0894e1abbd39d98ecaafead7d10b6a17919db2855", + "textPasses": { + "compass": 10, + "graphify": 9 + } + }, + "intermediate": { + "calls": 2101, + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "graphDigests": { + "compass": "c97352cfbf1b52b9b42af34108729b91e46113a2846ae49b4d39fc9c1336667b", + "graphify": "218aa21e02fad4506206c34acfc6d9ff58a2a0052615d92e4377c74bf977a937" + }, + "nodes": 656, + "questionsPerTool": 10, + "relationships": 3373, + "run": "go-initializer-cobra-01", + "runSha256": "b78c625258ba48c32cbf41758ad505bd62e2ba966ab8017726c20992abb06d60", + "textPasses": { + "compass": 10, + "graphify": 9 + } + } + }, + "finalDeltaFrom": { + "baseline": [], + "intermediate": [ + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 33122, + "endColumn": 17, + "endLine": 1153, + "file": "command.go", + "startByte": 33108, + "startColumn": 3, + "startLine": 1153 + }, + "siteText": "cmd.HelpFunc()", + "source": [ + "cobra.Command::ExecuteC", + "command.go", + 1084 + ], + "sourceLine": "\t\t\tcmd.HelpFunc()(cmd, args)", + "sourceSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90", + "status": "removed", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 17064, + "endColumn": 13, + "endLine": 521, + "file": "command.go", + "startByte": 17052, + "startColumn": 1, + "startLine": 521 + }, + "siteText": "c.HelpFunc()", + "source": [ + "cobra.Command::Help", + "command.go", + 520 + ], + "sourceLine": "\tc.HelpFunc()(c, []string{})", + "sourceSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90", + "status": "removed", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 15953, + "endColumn": 21, + "endLine": 479, + "file": "command.go", + "startByte": 15940, + "startColumn": 8, + "startLine": 479 + }, + "siteText": "c.UsageFunc()", + "source": [ + "cobra.Command::Usage", + "command.go", + 478 + ], + "sourceLine": "\treturn c.UsageFunc()(c)", + "sourceSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90", + "status": "removed", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 27634, + "endColumn": 26, + "endLine": 921, + "file": "command.go", + "startByte": 27617, + "startColumn": 9, + "startLine": 921 + }, + "siteText": "c.FlagErrorFunc()", + "source": [ + "cobra.Command::execute", + "command.go", + 905 + ], + "sourceLine": "\t\treturn c.FlagErrorFunc()(c, err)", + "sourceSha256": "f79613721ad04c9b438dfb30fe0ac3deca85c487658b0aa04a1ae6e4395bfd90", + "status": "removed", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + } + ] + }, + "repository": "cobra" + } + ], + "intermediateBinarySha256": "bd0168b9d1258199a23820c1888aba577a41869fec820baeb5d9a54db2a4087d", + "intermediateRuns": [ + { + "added": 2, + "baselineRun": "heldout-a-01", + "baselineRunSha256": "6412df38bbde56a1ec89974ac8d0a11162592bae90c403ed7b7f745acb6384bd", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "delta": [ + { + "multiplicity": 1, + "relation": "calls", + "site": { + "endByte": 436, + "endColumn": 22, + "endLine": 18, + "file": "context.go", + "startByte": 423, + "startColumn": 9, + "startLine": 18 + }, + "siteText": "rctx.URLParam", + "source": [ + "chi.URLParamFromCtx", + "context.go", + 16 + ], + "sourceLine": "\t\treturn rctx.URLParam(key)", + "status": "added", + "target": [ + "chi.Context::URLParam", + "context.go", + 124 + ] + }, + { + "multiplicity": 1, + "relation": "calls", + "site": { + "endByte": 10958, + "endColumn": 9, + "endLine": 364, + "file": "middleware/compress.go", + "startByte": 10951, + "startColumn": 2, + "startLine": 364 + }, + "siteText": "f.Flush", + "source": [ + "middleware.compressResponseWriter::Flush", + "middleware/compress.go", + 357 + ], + "sourceLine": "\t\tf.Flush()", + "status": "added", + "target": [ + "middleware.compressFlusher::Flush", + "middleware/compress.go", + 354 + ] + } + ], + "questionsPerTool": 11, + "removed": 0, + "repository": "chi", + "run": "go-initializer-chi-01", + "runSha256": "6aca80be34b6a893052302e20ce31ef11b4b88b272712e25e6b8ccea76efeb36", + "textPasses": { + "compass": 11, + "graphify": 10 + } + }, + { + "added": 4, + "baselineRun": "v2-shadow-05", + "baselineRunSha256": "1b05861d3f1b2d2deaba0b13cb3b9712ded14afa69a52fe1ca61df6cda2b67b6", + "commit": "adbc8813901bba65827259daa8e22ff94ec1f30e", + "delta": [ + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 33122, + "endColumn": 17, + "endLine": 1153, + "file": "command.go", + "startByte": 33108, + "startColumn": 3, + "startLine": 1153 + }, + "siteText": "cmd.HelpFunc()", + "source": [ + "cobra.Command::ExecuteC", + "command.go", + 1084 + ], + "sourceLine": "\t\t\tcmd.HelpFunc()(cmd, args)", + "status": "added", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 17064, + "endColumn": 13, + "endLine": 521, + "file": "command.go", + "startByte": 17052, + "startColumn": 1, + "startLine": 521 + }, + "siteText": "c.HelpFunc()", + "source": [ + "cobra.Command::Help", + "command.go", + 520 + ], + "sourceLine": "\tc.HelpFunc()(c, []string{})", + "status": "added", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 15953, + "endColumn": 21, + "endLine": 479, + "file": "command.go", + "startByte": 15940, + "startColumn": 8, + "startLine": 479 + }, + "siteText": "c.UsageFunc()", + "source": [ + "cobra.Command::Usage", + "command.go", + 478 + ], + "sourceLine": "\treturn c.UsageFunc()(c)", + "status": "added", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + }, + { + "multiplicity": 1, + "relation": "references", + "site": { + "endByte": 27634, + "endColumn": 26, + "endLine": 921, + "file": "command.go", + "startByte": 27617, + "startColumn": 9, + "startLine": 921 + }, + "siteText": "c.FlagErrorFunc()", + "source": [ + "cobra.Command::execute", + "command.go", + 905 + ], + "sourceLine": "\t\treturn c.FlagErrorFunc()(c, err)", + "status": "added", + "target": [ + "cobra.Command", + "command.go", + 54 + ] + } + ], + "questionsPerTool": 10, + "removed": 0, + "repository": "cobra", + "run": "go-initializer-cobra-01", + "runSha256": "b78c625258ba48c32cbf41758ad505bd62e2ba966ab8017726c20992abb06d60", + "textPasses": { + "compass": 10, + "graphify": 9 + } + } + ], + "reviews": [ + { + "judgment": "Supported call: the if initializer calls RouteContext, whose declared result is *Context; the selected URLParam method belongs to Context.", + "repository": "chi", + "site": "context.go:18" + }, + { + "judgment": "Supported interface-method call: comma-ok assertion at line 363 gives f the compressFlusher interface type. This does not establish the concrete runtime implementation. Other f bindings at 358 and 367 use http.Flusher.", + "repository": "chi", + "site": "middleware/compress.go:364" + }, + { + "judgment": "Regression: returned callbacks were projected as references to the factory receiver type Command. The outer invocation has no proven named target; retain the inner named factory call and omit the unsupported outer candidate. These four additions are not recall gains.", + "repository": "cobra", + "sites": [ + "command.go:479", + "command.go:521", + "command.go:921", + "command.go:1153" + ] + } + ], + "schema": "compass.go-receiver-development-review/1", + "scope": "Post-output development diagnostics on Chi and Cobra; not independent confirmation, population precision, or performance evidence.", + "verification": { + "clippy": "workspace lib/bin and same integration selection passed with warnings denied", + "native": { + "failed": 0, + "ignored": 2, + "passed": 1410, + "selection": "workspace lib/bin plus universal_evidence, universal_resolution, contracts, compass_product" + }, + "python": { + "passed": 95 + }, + "qualification": "running; do not infer a full gate pass from partial logs" + } +} diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index fbf975db9..58d606e78 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "3"; +pub const AST_CACHE_VERSION: &str = "4"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index caac1633a..ded47a8fb 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -988,6 +988,11 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< // Version 2 can contain calls attributed to a shadowed outer Rust // receiver. Those facts must not survive the semantics correction. fs::create_dir_all(cache_root.join("compass-out/cache/ast/v2/e1"))?; + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v3/e1"))?; + fs::write( + cache_root.join("compass-out/cache/ast/v3/e1/stale.msgpack"), + "stale Go receiver facts", + )?; fs::write( cache_root.join("compass-out/cache/ast/v2/e1/stale.msgpack"), "stale", @@ -1020,6 +1025,7 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< ); assert!(!cache_root.join("compass-out/cache/ast/v0.9.21").exists()); assert!(!cache_root.join("compass-out/cache/ast/v2").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v3").exists()); let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index b55b94ce8..765183efe 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -1034,7 +1034,6 @@ struct DirectEvidenceState<'source> { go_return_types: HashMap>>, go_member_types: HashMap<(String, String), String>, go_collection_element_types: HashMap, - go_collection_binding_element_types: HashMap>, go_range_return_types: HashMap>>, go_range_member_types: HashMap<(String, String), String>, java_containers: HashMap, @@ -1126,7 +1125,6 @@ impl<'source> DirectEvidenceState<'source> { go_return_types: HashMap::new(), go_member_types: HashMap::new(), go_collection_element_types: HashMap::new(), - go_collection_binding_element_types: HashMap::new(), go_range_return_types: HashMap::new(), go_range_member_types: HashMap::new(), java_containers: HashMap::new(), @@ -7069,21 +7067,8 @@ impl<'source> DirectEvidenceState<'source> { continue; }; if parameter.kind() == "variadic_parameter_declaration" { - let Some(element_target) = go_direct_type_target(type_node) else { - continue; - }; - let Some(element_type) = self.go_qualified_type_target(owner, element_target) - else { - continue; - }; - self.go_collection_binding_element_types - .entry(owner.scope_id.clone()) - .or_default() - .extend( - names - .into_iter() - .map(|(name, _)| (name, element_type.clone())), - ); + // Its element type is read from this exact parameter AST + // when resolving a range; the slice itself is not a receiver. continue; } let mut targets = Vec::new(); @@ -7474,6 +7459,12 @@ impl<'source> DirectEvidenceState<'source> { let Some(function) = function else { return Ok(()); }; + if self.language == "go" && function.kind() == "call_expression" { + // factory()() invokes an unnamed returned callback. Its receiver + // binding cannot identify the callback or a type conversion. The + // walker visits the inner factory call separately. + return Ok(()); + } let raw = self.text(function); let (qualifier, spelling) = split_qualified(&raw); if spelling.is_empty() { @@ -7603,6 +7594,18 @@ impl<'source> DirectEvidenceState<'source> { None }; let binding = call_result_binding.or_else(|| { + if self.language == "go" + && qualifier.is_some() + && let Some((value, _)) = + go_local_initializer_with_index_before(function, binding_name, self.source) + && !value + .parent() + .is_some_and(|parent| parent.kind() == "parameter_declaration") + { + // Parameter aliases and package imports carry useful project + // evidence. A nearer local/range binding must not reuse them. + return None; + } self.binding_for_occurrence( owner, binding_name, @@ -7619,7 +7622,9 @@ impl<'source> DirectEvidenceState<'source> { } else { qualifier .and_then(|qualifier| { - self.local_target_for(owner, qualifier) + (self.language != "go") + .then(|| self.local_target_for(owner, qualifier)) + .flatten() .map(|target| format!("{target}::{spelling}")) }) .or_else(|| { @@ -7630,6 +7635,9 @@ impl<'source> DirectEvidenceState<'source> { }) .or_else(|| { qualifier + .filter(|qualifier| { + self.language != "go" || !self.go_name_is_locally_bound(call, qualifier) + }) .and_then(|qualifier| { self.imported_qualified_target_for( owner, @@ -8179,6 +8187,11 @@ impl<'source> DirectEvidenceState<'source> { match function.kind() { "identifier" => { let spelling = self.text(function); + if go_local_initializer_with_index_before(function, &spelling, self.source) + .is_some() + { + return None; + } self.imported_target_for_occurrence(owner, &spelling, function.start_byte(), true) .cloned() .or_else(|| Some(format!("{}.{}", self.module_or_package, spelling))) @@ -8193,6 +8206,15 @@ impl<'source> DirectEvidenceState<'source> { (operand.kind() == "identifier") .then(|| self.text(operand)) .and_then(|package| { + if go_local_initializer_with_index_before( + operand, + &package, + self.source, + ) + .is_some() + { + return None; + } self.imported_target_for_occurrence( owner, &package, @@ -8257,12 +8279,10 @@ impl<'source> DirectEvidenceState<'source> { if depth >= GO_TYPE_INFERENCE_DEPTH_LIMIT || !visited.insert(name.to_owned()) { return None; } - let result = go_enclosing_range_value(use_node, name, self.source) - .and_then(|range| self.go_range_expression_type(owner, range, depth + 1, visited)) - .or_else(|| self.local_target_for(owner, name).cloned()) - .or_else(|| { - let (initializer, output_index) = - go_local_initializer_with_index_before(use_node, name, self.source)?; + // The nearest lexical binding owns the receiver, including an unknown + // type. Never fall through to an outer parameter after a local shadow. + let result = go_local_initializer_with_index_before(use_node, name, self.source).and_then( + |(initializer, output_index)| { self.go_expression_type_at_output( owner, initializer, @@ -8270,7 +8290,8 @@ impl<'source> DirectEvidenceState<'source> { visited, output_index, ) - }); + }, + ); visited.remove(name); result } @@ -8300,6 +8321,11 @@ impl<'source> DirectEvidenceState<'source> { "type_identifier" | "qualified_type" | "pointer_type" | "slice_type" | "array_type" | "map_type" | "channel_type" => go_direct_type_target(expression) .and_then(|target| self.go_qualified_type_target(owner, target)), + "range_clause" if output_index == Some(1) => expression + .child_by_field_name("right") + .and_then(|collection| { + self.go_range_expression_type(owner, collection, depth + 1, visited) + }), "identifier" => self.go_local_value_type_inner( owner, expression, @@ -8332,19 +8358,8 @@ impl<'source> DirectEvidenceState<'source> { } "call_expression" => { let function = expression.child_by_field_name("function")?; - let qualified_callable = match function.kind() { - "identifier" => { - format!("{}.{}", self.module_or_package, self.text(function)) - } - "selector_expression" => { - let operand = function.child_by_field_name("operand")?; - let field = function.child_by_field_name("field")?; - let receiver = - self.go_expression_type(owner, operand, depth + 1, visited)?; - format!("{receiver}::{}", self.text(field)) - } - _ => return None, - }; + let qualified_callable = + self.go_callable_qualified_name(owner, function, depth + 1, visited)?; self.go_return_types .get(&qualified_callable) .and_then(|types| go_output_type(types, output_index)) @@ -8384,33 +8399,25 @@ impl<'source> DirectEvidenceState<'source> { if !visited.insert(name.clone()) { return None; } - let result = self - .local_target_for(owner, &name) - .and_then(|collection| self.go_collection_element_types.get(collection)) - .cloned() - .or_else(|| { - self.local_value_for( - &self.go_collection_binding_element_types, + let result = go_local_initializer_with_index_before(expression, &name, self.source) + .and_then(|(initializer, output_index)| { + self.go_range_expression_type_at_output( owner, - &name, + initializer, + depth + 1, + visited, + output_index, ) - .cloned() - }) - .or_else(|| { - go_local_initializer_with_index_before(expression, &name, self.source) - .and_then(|(initializer, output_index)| { - self.go_range_expression_type_at_output( - owner, - initializer, - depth + 1, - visited, - output_index, - ) - }) }); visited.remove(&name); result } + "type_identifier" | "qualified_type" | "pointer_type" | "slice_type" | "array_type" + | "map_type" | "channel_type" => self.go_collection_element_type(owner, expression), + "variadic_parameter_declaration" => expression + .child_by_field_name("type") + .and_then(go_direct_type_target) + .and_then(|target| self.go_qualified_type_target(owner, target)), "call_expression" => { let function = expression.child_by_field_name("function")?; if function.kind() == "identifier" && self.text(function) == "make" { @@ -8418,19 +8425,8 @@ impl<'source> DirectEvidenceState<'source> { let collection_type = arguments.named_child(0)?; return self.go_collection_element_type(owner, collection_type); } - let qualified_callable = match function.kind() { - "identifier" => { - format!("{}.{}", self.module_or_package, self.text(function)) - } - "selector_expression" => { - let operand = function.child_by_field_name("operand")?; - let field = function.child_by_field_name("field")?; - let receiver = - self.go_expression_type(owner, operand, depth + 1, visited)?; - format!("{receiver}::{}", self.text(field)) - } - _ => return None, - }; + let qualified_callable = + self.go_callable_qualified_name(owner, function, depth + 1, visited)?; self.go_range_return_types .get(&qualified_callable) .and_then(|types| go_output_type(types, output_index)) @@ -11194,51 +11190,6 @@ fn go_range_value_type_target(node: Node<'_>) -> Option> { } } -fn go_enclosing_range_value<'tree>( - use_node: Node<'tree>, - name: &str, - source: &[u8], -) -> Option> { - fn named_children(node: Node<'_>) -> Vec> { - let mut cursor = node.walk(); - node.children(&mut cursor) - .filter(|child| child.is_named()) - .collect() - } - - let mut ancestor = use_node.parent(); - while let Some(node) = ancestor { - if node.kind() == "for_statement" - && let Some(range) = named_children(node) - .into_iter() - .find(|child| child.kind() == "range_clause") - && let Some(left) = range.child_by_field_name("left") - { - let variables = if left.kind() == "expression_list" { - named_children(left) - } else { - vec![left] - }; - let matching_variable = variables.iter().position(|variable| { - variable.kind() == "identifier" && variable.utf8_text(source).ok() == Some(name) - }); - if let Some(index) = matching_variable { - return (variables.len() == 2 && index == 1) - .then(|| range.child_by_field_name("right")) - .flatten(); - } - } - if matches!( - node.kind(), - "function_declaration" | "method_declaration" | "func_literal" - ) { - break; - } - ancestor = node.parent(); - } - None -} - fn has_descendant(node: Node<'_>, kind: &str) -> bool { if node.kind() == kind { return true; @@ -11311,7 +11262,9 @@ fn go_local_initializer_with_index_before<'tree>( if names.len() > 1 && values.len() == 1 && values[0].kind() == "call_expression" { return Some((values[0], u32::try_from(index).ok())); } - values.get(index).copied().map(|value| (value, None)) + // A declared name with an unsupported initializer still shadows an + // outer binding. Return an uninterpreted node rather than falling out. + Some((values.get(index).copied().unwrap_or(names[index]), None)) } fn in_statement<'tree>( @@ -11337,7 +11290,9 @@ fn go_local_initializer_with_index_before<'tree>( if let Some(type_node) = statement.child_by_field_name("type") { return Some((type_node, None)); } - let values = statement.child_by_field_name("value")?; + let Some(values) = statement.child_by_field_name("value") else { + return Some((statement, None)); + }; let values = if values.kind() == "expression_list" { named_children(values) } else { @@ -11346,9 +11301,9 @@ fn go_local_initializer_with_index_before<'tree>( if names.len() > 1 && values.len() == 1 && values[0].kind() == "call_expression" { return Some((values[0], u32::try_from(index).ok())); } - values.get(index).copied().map(|value| (value, None)) + Some((values.get(index).copied().unwrap_or(statement), None)) } - "var_declaration" => named_children(statement) + "var_declaration" | "var_spec_list" => named_children(statement) .into_iter() .rev() .find_map(|child| in_statement(child, name, source)), @@ -11359,7 +11314,24 @@ fn go_local_initializer_with_index_before<'tree>( let use_start = use_node.start_byte(); let mut ancestor = use_node.parent(); while let Some(scope) = ancestor { - let for_initializer = if scope.kind() == "for_clause" { + if scope.kind() == "type_switch_statement" + && let Some(alias) = scope.child_by_field_name("alias") + && scope + .child_by_field_name("value") + .is_some_and(|value| value.end_byte() <= use_start) + && (alias.utf8_text(source).ok() == Some(name) + || named_children(alias) + .iter() + .any(|node| node.utf8_text(source).ok() == Some(name))) + { + // Case-specific narrowing is not inferred here. The case binding + // still shadows the outer name and cannot borrow its type. + return Some((alias, None)); + } + let control_initializer = if matches!( + scope.kind(), + "for_clause" | "if_statement" | "expression_switch_statement" | "type_switch_statement" + ) { scope.child_by_field_name("initializer") } else if scope.kind() == "for_statement" { let mut cursor = scope.walk(); @@ -11370,12 +11342,37 @@ fn go_local_initializer_with_index_before<'tree>( } else { None }; - if let Some(initializer) = for_initializer + if let Some(initializer) = control_initializer && initializer.end_byte() <= use_start && let Some(found) = in_statement(initializer, name, source) { return Some(found); } + if scope.kind() == "for_statement" { + let range = named_children(scope) + .into_iter() + .find(|child| child.kind() == "range_clause"); + if let Some(range) = range + && range.end_byte() <= use_start + && let Some(left) = range.child_by_field_name("left") + { + let mut cursor = range.walk(); + let declares = range + .children(&mut cursor) + .any(|child| child.kind() == ":="); + let names = named_children(left); + if declares + && let Some(index) = names + .iter() + .position(|n| n.utf8_text(source).ok() == Some(name)) + { + // Only the second variable of a two-value range has the + // existing element-type proof. Keys and other forms stay + // unknown, but still block outer receiver types. + return Some((range, (names.len() == 2 && index == 1).then_some(1))); + } + } + } if matches!(scope.kind(), "block" | "statement_list") { let mut statements = named_children(scope); statements.reverse(); @@ -11387,8 +11384,31 @@ fn go_local_initializer_with_index_before<'tree>( return Some(initializer); } } - if matches!(scope.kind(), "function_declaration" | "method_declaration") { - break; + if matches!( + scope.kind(), + "function_declaration" | "method_declaration" | "func_literal" + ) { + for field in ["receiver", "parameters", "result"] { + if let Some(parameters) = scope.child_by_field_name(field) { + for parameter in named_children(parameters) { + let mut cursor = parameter.walk(); + if parameter + .children_by_field_name("name", &mut cursor) + .any(|n| n.utf8_text(source).ok() == Some(name)) + { + let value = if parameter.kind() == "variadic_parameter_declaration" { + parameter + } else { + parameter.child_by_field_name("type").unwrap_or(parameter) + }; + return Some((value, None)); + } + } + } + } + if scope.kind() != "func_literal" { + break; + } } ancestor = scope.parent(); } diff --git a/crates/compass-languages/tests/universal_evidence.rs b/crates/compass-languages/tests/universal_evidence.rs index bb4a8fff2..82a1bdbdc 100644 --- a/crates/compass-languages/tests/universal_evidence.rs +++ b/crates/compass-languages/tests/universal_evidence.rs @@ -1914,3 +1914,85 @@ fn direct_evidence_ids_and_partial_diagnostics_are_deterministic() { .any(|diagnostic| diagnostic.code == "partial_parser_recovery") ); } + +#[test] +fn go_control_receiver_evidence_uses_the_nearest_binding_and_factory_identity() { + let source = br#"package sample +type Good struct{} +func (*Good) Run() {} +type Bad struct{} +func (*Bad) Run() {} +func Factory() *Good { return nil } +func caller(current *Bad, Factory func() *Bad, value any) { + if current := Factory(); current != nil { current.Run() } // unknown + { var current any; current.Run() } // unknown + switch current := value.(type) { case *Good: current.Run() } // unknown + current.Run() // sample.Bad::Run +} +func direct(current *Bad, values []*Good) { + if current := Factory(); current != nil { current.Run() } // sample.Good::Run + switch current := Factory(); { default: current.Run() } // sample.Good::Run + for _, current := range values { + current.Run() // sample.Good::Run + { var current *Bad; current.Run() } // sample.Bad::Run + current.Run() // sample.Good::Run + } + current.Run() // sample.Bad::Run +} +"#; + let evidence = Engine::default() + .extract_source_combined( + std::path::Path::new("/repo/sample/receiver.go"), + "sample/receiver.go", + source, + ) + .expect("extract Go receiver evidence") + .graph + .semantic_evidence + .expect("Go evidence"); + validate_evidence(&evidence, EvidenceLimits::default()).expect("valid evidence"); + for occurrence in evidence + .occurrences + .iter() + .filter(|o| o.spelling == "Run" && o.role == SemanticRole::Call) + { + let line = std::str::from_utf8(source) + .expect("source") + .lines() + .nth(usize::try_from(occurrence.range.start_line - 1).expect("line index")) + .expect("source line"); + let expected = line.rsplit_once("// ").expect("reviewed call").1; + let candidate = evidence + .candidates + .iter() + .find(|c| c.occurrence_id.as_deref() == Some(&occurrence.id)) + .expect("candidate"); + if expected == "unknown" { + assert!( + candidate.constraints.qualified_name.is_none(), + "{line}: {candidate:?}" + ); + assert!( + candidate.binding_id.is_none(), + "{line}: must not reuse outer receiver or factory" + ); + } else { + assert_eq!( + candidate.constraints.qualified_name.as_deref(), + Some(expected), + "{line}" + ); + } + let start = usize::try_from(occurrence.range.start_byte).expect("start"); + let end = usize::try_from(occurrence.range.end_byte).expect("end"); + assert_eq!(&source[start..end], b"current.Run"); + } + assert_eq!( + evidence + .occurrences + .iter() + .filter(|o| o.spelling == "Run" && o.role == SemanticRole::Call) + .count(), + 10 + ); +} diff --git a/crates/compass-resolve/tests/universal_resolution/go.rs b/crates/compass-resolve/tests/universal_resolution/go.rs index f5128bbf5..4ccacd1d5 100644 --- a/crates/compass-resolve/tests/universal_resolution/go.rs +++ b/crates/compass-resolve/tests/universal_resolution/go.rs @@ -1,3 +1,33 @@ +#[test] +fn go_returned_callback_invocation_does_not_reference_receiver_type() { + let source = br#"package pkg +type Command struct{} +func (*Command) Callback() func() { return nil } +func caller(command *Command) { + command.Callback()() +} +"#; + let extracted = extract("pkg/callback.go", source); + let sources = HashMap::from([( + "pkg/callback.go".to_owned(), + String::from_utf8(source.to_vec()).expect("source"), + )]); + let resolved = compass_resolve::resolve(&[extracted], &sources); + let callback = resolved + .nodes + .iter() + .find(|node| node.string("qualified_name") == "pkg.Command::Callback") + .expect("callback factory declaration"); + let site_edges: Vec<_> = resolved + .edges + .iter() + .filter(|edge| edge.string("source_location") == "L5") + .collect(); + assert_eq!(site_edges.len(), 1, "only the inner factory call is known"); + assert_eq!(site_edges[0].target, callback.id); + assert_eq!(site_edges[0].string("relation"), "calls"); +} + #[test] fn go_closures_resolve_typed_parameters_and_captured_receivers() { let go_source = br#"package pkg @@ -574,3 +604,117 @@ func (body *Body) Encode() { edge.string("relation") != "calls" || edge.string("source_location") != "L6" })); } + +#[test] +fn go_control_initializers_resolve_occurrences_without_leaking_shadowed_types() { + let definitions = br#"package pkg +type Runner interface { Run() } +type Good struct{} +func (*Good) Run() {} +type Bad struct{} +func (*Bad) Run() {} +func Factory() *Good { return nil } +func Other() *Bad { return nil } +"#; + let source = br#"package pkg +func Use(current *Bad, Factory func() Runner) { + current.Run() // bad + if current := Other(); current != nil { + current.Run() // bad + } + if current := Factory(); current != nil { + current.Run() // unknown callback + } + current.Run() // bad +} +func Direct(current *Bad) { + if current := Factory(); current != nil { + current.Run() // good + current.Run() // good + } else { + current.Run() // good + } + current.Run() // bad + switch current := Factory(); { + default: current.Run() // good + } + for current := Factory(); current != nil; { + current.Run() // good + break + } + { + current := Factory() + current.Run() // good + callback := func(current *Bad) { current.Run() } // bad + _ = callback + } + current.Run() // bad +} +"#; + let inputs = vec![ + extract("pkg/definitions.go", definitions), + extract("pkg/caller.go", source), + ]; + let sources = HashMap::from([ + ( + "pkg/definitions.go".to_owned(), + String::from_utf8(definitions.to_vec()).expect("definitions"), + ), + ( + "pkg/caller.go".to_owned(), + String::from_utf8(source.to_vec()).expect("caller"), + ), + ]); + let first = compass_resolve::resolve(&inputs, &sources); + let second = compass_resolve::resolve(&[inputs[1].clone(), inputs[0].clone()], &sources); + assert_eq!(universal_edges(&first), universal_edges(&second)); + let good = first + .nodes + .iter() + .find(|n| n.string("qualified_name") == "pkg.Good::Run") + .expect("Good.Run"); + let bad = first + .nodes + .iter() + .find(|n| n.string("qualified_name") == "pkg.Bad::Run") + .expect("Bad.Run"); + for (index, line) in std::str::from_utf8(source) + .expect("source") + .lines() + .enumerate() + { + let expected = if line.ends_with("// good") { + Some(&good.id) + } else if line.ends_with("// bad") { + Some(&bad.id) + } else { + None + }; + if !line.contains(".Run()") { + continue; + } + let calls = first + .edges + .iter() + .filter(|e| { + e.string("relation") == "calls" + && e.string("source_file") == "pkg/caller.go" + && e.string("source_location") == format!("L{}", index + 1) + }) + .collect::>(); + let matches = calls + .iter() + .filter(|e| e.target == good.id || e.target == bad.id) + .collect::>(); + if let Some(target) = expected { + assert_eq!(matches.len(), 1, "line {}: {line}; {matches:?}", index + 1); + assert_eq!(&matches[0].target, target, "line {}: {line}", index + 1); + assert!(matches[0].string("extractor").contains(".universal")); + } else { + assert!( + matches.is_empty(), + "callback must not select a same-named factory: {matches:?}" + ); + } + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 5b57806d4..3f0a33724 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1169,6 +1169,77 @@ and macOS linker warnings remain in build/test logs. Extraction qualification and JavaScript gates were not rerun: this change only projects query ambiguity and does not change extraction, publication, or viewer assets. +### Development correction: Go control initializers and receiver shadowing + +The registered Chi miss at `context.go:18` came from a supported factory return +that was lost in an `if` initializer. The universal Go producer now searches +control initializers and nearer lexical bindings before outer parameter aliases. +Unknown locals, callback factories, closure parameters, and type-switch aliases +must not borrow a same-named outer type or package function. Case-specific +type-switch narrowing remains unsupported. Existing parameter/import evidence +still supports project-wide field and return resolution. Disposable AST cache +semantics advance from 3 to 4; users must rebuild old Go graphs to get these +edges. Published history is unchanged. + +The first frozen development binary is retained under +`go-initializer-provenance`, SHA256 +`bd0168b9d1258199a23820c1888aba577a41869fec820baeb5d9a54db2a4087d`. +Fresh paired runs `go-initializer-chi-01` and `go-initializer-cobra-01` use +unchanged question suites and pinned sources. Text scores remain **Chi 11/11 +versus 10/11**, and **Cobra 10/10 versus 9/10** (Compass versus Graphify). +These scores did not improve. Concurrent builds make these runs unsuitable +for latency claims. The Graphify launcher/package provenance limitation above +still applies. + +Every graph relationship delta was inspected, using source/target declaration +identity, relationship kind, exact site, and multiplicity. Chi adds two calls +and removes no relationships: + +- `URLParamFromCtx` to `Context.URLParam` at `context.go:18`, supported by + `RouteContext`'s declared `*Context` return and the `if` initializer. +- `compressResponseWriter.Flush` to the `compressFlusher.Flush` interface method + at `middleware/compress.go:364`, supported by the type assertion at line 363. + This proves an interface-method target, not the runtime implementation. + +The four original Chi pair/occurrence witnesses now all match both tools; +Compass previously matched three. A separately recorded **post-output** fifth +witness checks the compression interface call: Compass matches it; Graphify +has both unique endpoints but lacks the call. This diagnostic selection is +not an independent precision or recall sample, and does not replace panel A. + +The same complete-delta review caught a regression on Cobra: four new type +references to `Command` at `command.go:479,521,921,1153` came from invoking +callbacks returned by `UsageFunc`, `HelpFunc`, and `FlagErrorFunc`. The outer +invocation has no proven named target. A native regression reproduced the +extra edge (two site edges instead of one). The producer now omits that +unsupported outer call candidate while retaining the separately visited inner +factory call. The intermediate binary and its faulty output remain recorded; +the four references are not counted as gains. + +The checked-in `go_receiver_development_review.json` records intermediate run +digests, every delta, source anchors, and judgments. +`edge_witnesses_go_receiver_chi_diagnostic.json` preserves the expanded diagnostic +separately from the original registered witness file. + +The corrected frozen binary has SHA256 +`bb09a5335c80fd7dd710e008b4a2f30f9403108e4883ed74e7d7c0de1b0aaca9` +under `go-initializer-final-provenance`. Fresh paired repeats +`go-initializer-chi-02` and `go-initializer-cobra-02` complete all 42 requests. +Chi retains exactly the two reviewed additions; Cobra removes exactly the four +intermediate false references and has no relationship delta from its baseline. +Text scores and the five-pair diagnostic result are unchanged. The delta script, +graph/run digests, source excerpts, and both failed/passing callback regression +logs are retained. These are development results after panel A. + +Final-source native checks pass **1,410 tests, zero failed, two ignored**: +workspace library/binary tests plus `universal_evidence`, `universal_resolution`, +`contracts`, and `compass_product`. Workspace and the same integration Clippy +selection pass with warnings denied. All **95 Python tests**, formatting, +product boundary, and diff checks pass. Qualification is still running; a full +fixture-gate pass is not yet claimed. Logs use `go-initializer-native-03`, +`go-initializer-clippy-02`, and `go-initializer-python-02` prefixes. Existing +core unused-mut and macOS linker warnings remain in build/test logs. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index 5cf2b7c9a..f4be59dd7 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -460,6 +460,19 @@ or file; publishing such an edge would invent a return contract and can create an invalid file-to-type relationship. Named functions, methods, and interface methods continue to publish their result types as `returns` evidence. +Go selector receivers follow the nearest supported lexical binding, including +`if`, expression-switch, and loop initializers, block variables, range values, +and closure parameters. Initializers are visible only after their declaration; +control bindings do not escape their statement. An unknown nearer type blocks +an outer parameter alias. Factory results require a source-resolved callable; +a local callback or shadowed import cannot borrow a same-named global factory. +Parameter/import bindings still carry project evidence for cross-file fields +and return contracts. Type-switch aliases block outer types but case-specific +narrowing remains unsupported. In `factory()()`, only the named inner factory +call is eligible for resolution; the unnamed returned callback cannot borrow +the factory receiver's type as its target. Receiver inference keeps its bounded +depth and does not select methods solely because their terminal names match. + Python file imports are visible at module scope. Function- and class-local imports are indexed only in their owning lexical scope, so they cannot leak to sibling functions or become file-owned facts. Each imported item retains its From 3ec3732a989e5d2cf51aa55438450faea7f84e76 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 18:56:52 -0700 Subject: [PATCH 26/97] bench: register panel A MCP community and hub comparison --- benchmarks/agent_query/mcp_audit.py | 6 +- benchmarks/agent_query/mcp_compare.py | 15 ++++- benchmarks/agent_query/suite_mcp_panel_a.json | 65 +++++++++++++++++++ .../agent_query/tests/test_mcp_audit.py | 14 ++++ 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 benchmarks/agent_query/suite_mcp_panel_a.json diff --git a/benchmarks/agent_query/mcp_audit.py b/benchmarks/agent_query/mcp_audit.py index 8e97b3302..2a2ab99a3 100644 --- a/benchmarks/agent_query/mcp_audit.py +++ b/benchmarks/agent_query/mcp_audit.py @@ -9,7 +9,7 @@ from pathlib import Path import re -from benchmarks.agent_query.mcp_compare import community +from benchmarks.agent_query.mcp_compare import captured_repository, community from benchmarks.agent_query.path_audit import read_bounded, MAX_GRAPH_BYTES from benchmarks.agent_query.runner import _node_anchor, _sha256_file @@ -124,10 +124,10 @@ def main(args): graphs={} for row in run['results']: key=(row['repository'],row['tool']) - if key[0] not in {'cobra','flask','gson','zod','axum'} or key[1] not in {'compass','graphify'}: + repo = captured_repository(source, key[0]) + if key[1] not in {'compass','graphify'}: raise ValueError('invalid capture key') if key not in graphs: - repo=next(r for r in source['repositories'] if r['repository']==key[0]) p=Path(repo[key[1]+'Graph']) if _sha256_file(p)!=row['graphSha256']:raise ValueError('graph digest mismatch') graphs[key]=json.loads(read_bounded(p,MAX_GRAPH_BYTES)) diff --git a/benchmarks/agent_query/mcp_compare.py b/benchmarks/agent_query/mcp_compare.py index ff5826d32..e97a77778 100644 --- a/benchmarks/agent_query/mcp_compare.py +++ b/benchmarks/agent_query/mcp_compare.py @@ -8,6 +8,7 @@ import argparse import json from pathlib import Path +import re import shutil import time from collections import Counter @@ -22,6 +23,16 @@ def community(node, tool): return value.get('id') if tool == 'compass' and isinstance(value, dict) else value +def captured_repository(source_run, name): + """Resolve one captured repository without allowing raw-output path escapes.""" + if not isinstance(name, str) or re.fullmatch(r'[a-z0-9][a-z0-9_-]{0,79}', name) is None: + raise ValueError('invalid repository key') + matches = [r for r in source_run['repositories'] if r['repository'] == name] + if len(matches) != 1: + raise ValueError('repository must occur exactly once in the captured run') + return matches[0] + + def prepare_questions(graph, tool, witness): if 'pathQuestions' in witness: if not isinstance(witness['pathQuestions'], list) or not 1 <= len(witness['pathQuestions']) <= 32: @@ -110,9 +121,7 @@ def execute(args): report['servers'][tool] = {'executable':str(binary.resolve()), 'executableSha256':_sha256_file(binary)} for witness in manifest['repositories']: name = witness['name'] - if name not in {'cobra','flask','gson','zod','axum'}: - raise ValueError('unsupported repository key') - repo = next(r for r in source_run['repositories'] if r['repository'] == name) + repo = captured_repository(source_run, name) pinned = next(r for r in suite.repositories if r.name == name) source = Path(repo['source']) _verify_source(pinned,source) diff --git a/benchmarks/agent_query/suite_mcp_panel_a.json b/benchmarks/agent_query/suite_mcp_panel_a.json new file mode 100644 index 000000000..6d4ba25c5 --- /dev/null +++ b/benchmarks/agent_query/suite_mcp_panel_a.json @@ -0,0 +1,65 @@ +{ + "schema": "compass.mcp-comparison-inputs/1", + "scope": "Development MCP extension on panel A after its CLI outputs were observed. Use original frozen panel-A graphs and the hub-evidence Compass binary; commit these questions before MCP execution. Prepared IDs are not scored as node retrieval. Counts and membership measure stored-graph consistency, not source precision, functional community quality, or god-object diagnosis.", + "policy": { + "god_nodes": "Return top 10 hubs. Independently check displayed degree against distinct stored directed endpoint pairs, counting self-loops twice. Ambiguous labels do not verify identity. Report source-backed declarations separately; degree does not prove a god-object defect.", + "get_community": "Return every member of the largest stored community; break size ties by smallest numeric ID. Compare the exact multiset of displayed labels and source files with that tool's graph. Different partitions are allowed. Also query a community ID above the stored maximum and require not found.", + "get_neighbors": "Return calls adjacent to the exact source declaration identified below, using its tool-specific exact ID as input. Compare distinct direction/neighbor/relation triples with stored calls. Separately report repeated-site information and ambiguous labels. ID lookup is preparation, not a measured retrieval success.", + "ambiguous_neighbors": "The same unqualified label denotes multiple source declarations. Require explicit ambiguity or a candidate list; selecting one neighbor list silently fails.", + "graph_stats": "Compare node and community counts with the captured graph, and edge count with stored edge records. Report representation discrepancies rather than treating more edges as better.", + "bounds": "60 seconds per RPC; 16 MiB per response/stderr and 64 MiB per session. For full membership/neighbor enumeration, Graphify receives token_budget=262144; Compass exposes no equivalent option. Record truncation, requested bounds, actual text bytes, and protocol overhead separately. This arm measures complete enumeration, not equal 2000-token answers.", + "source_review": "Review every returned hub identity for source location and inspect its declared role. Review neighbor call directions against the previously reviewed source facts. Functional cluster and god-object judgments remain separate and cannot be inferred from consistency scores." + }, + "repositories": [ + { + "name": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "file": "context.go", + "line": 16, + "symbol": "URLParamFromCtx", + "sourceText": "func URLParamFromCtx(ctx context.Context, key string) string {", + "sourceFileSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "ambiguousLabel": "URLParam" + }, + { + "name": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "file": "src/click/_compat.py", + "line": 374, + "symbol": "open_stream", + "sourceText": "def open_stream(", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "ambiguousLabel": "close" + }, + { + "name": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "symbol": "isValidBodyHtml", + "sourceText": "public boolean isValidBodyHtml(String bodyHtml) {", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "ambiguousLabel": "isValid" + }, + { + "name": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "file": "src/createStore.ts", + "line": 201, + "symbol": "subscribe", + "sourceText": "function subscribe(listener: () => void) {", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "ambiguousLabel": "subscribe" + }, + { + "name": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "file": "src/lib.rs", + "line": 456, + "symbol": "sort_by_file_name", + "sourceText": "pub fn sort_by_file_name(self) -> Self {", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "ambiguousLabel": "sort_by" + } + ] +} diff --git a/benchmarks/agent_query/tests/test_mcp_audit.py b/benchmarks/agent_query/tests/test_mcp_audit.py index aca28d6d2..dc8fd798c 100644 --- a/benchmarks/agent_query/tests/test_mcp_audit.py +++ b/benchmarks/agent_query/tests/test_mcp_audit.py @@ -3,6 +3,20 @@ class McpAuditTests(unittest.TestCase): + def test_new_panel_repository_must_be_captured_exactly_once(self): + from benchmarks.agent_query.mcp_compare import captured_repository + record = {'repository': 'chi'} + self.assertEqual(captured_repository({'repositories': [record]}, 'chi'), record) + for records in [[], [record, record]]: + with self.assertRaises(ValueError): + captured_repository({'repositories': records}, 'chi') + + def test_repository_keys_cannot_escape_raw_capture_directory(self): + from benchmarks.agent_query.mcp_compare import captured_repository + for name in ['../chi', '/chi', 'chi/other', 'chi\\other', '.', '', 'a' * 81, None]: + with self.subTest(name=name), self.assertRaises(ValueError): + captured_repository({'repositories': [{'repository': name}]}, name) + def test_connection_failure_does_not_remove_remaining_questions(self): from benchmarks.agent_query.mcp_compare import skipped_results questions=[('hubs','god_nodes',{'top_n':10}),('community','get_community',{'community_id':0})] From a29f29883b6b5a421eb45e2802e06267c7a9541f Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 19:21:44 -0700 Subject: [PATCH 27/97] fix: prevent filesystem hierarchy from inventing programmatic route parents --- CHANGELOG.md | 5 + COMPATIBILITY.md | 11 + MIGRATION.md | 6 + benchmarks/agent_query/README.md | 9 + ...nesses_chi_route_hierarchy_diagnostic.json | 27 + .../go_receiver_development_review.json | 5 +- .../agent_query/hub_role_reviews_panel_a.json | 1310 +++++++++++++++++ ...hub_role_reviews_panel_a_after_routes.json | 1310 +++++++++++++++++ .../agent_query/mcp_panel_a_review.json | 485 ++++++ crates/compass-core/src/build_state.rs | 1 + .../compass-languages/src/frameworks/mod.rs | 6 +- .../compass-resolve/src/frameworks/routes.rs | 46 +- .../tests/framework_qualification.rs | 9 +- .../compass-resolve/tests/framework_routes.rs | 87 ++ ...ode-graph-intelligence-audit-2026-09-26.md | 132 +- docs/reference/react-framework-graph.md | 6 + 16 files changed, 3441 insertions(+), 14 deletions(-) create mode 100644 benchmarks/agent_query/edge_witnesses_chi_route_hierarchy_diagnostic.json create mode 100644 benchmarks/agent_query/hub_role_reviews_panel_a.json create mode 100644 benchmarks/agent_query/hub_role_reviews_panel_a_after_routes.json create mode 100644 benchmarks/agent_query/mcp_panel_a_review.json diff --git a/CHANGELOG.md b/CHANGELOG.md index ba7ea1420..623a4d30c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Restrict filesystem route hierarchy to recognized file-route conventions. + Independent programmatic routers no longer acquire containment edges merely + from shared receiver names and source directories, which could inflate hubs + and create unsupported navigation paths. + - Resolve Go receivers introduced by `if` and switch initializers using their source-proven factory results. Respect nearer locals, range bindings, and closure parameters instead of borrowing an outer receiver type; do not diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index b91edb987..74471833a 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -219,6 +219,17 @@ from 3 to 4, invalidating older disposable AST facts across languages. Producer capabilities and evidence/graph schemas are unchanged; published history remains immutable. +### Framework route hierarchy + +Framework route hierarchy now requires a recognized filesystem-convention fact +from its owning framework producer. A receiver name such as `r` or `app` does +not establish parentage between programmatic routes in separate source files. +Framework composition rules still own programmatic mounts and groups. The +framework-pack semantics identity advances from 6 to 7, and build-state seals +now include that identity. Rebuild existing graphs to remove unsupported +containment edges and recompute affected paths, degrees, and communities. +Graph/evidence schema majors and immutable historical realizations are unchanged. + ### Agent Query View Compass adds the additive strict projection `compass.query.agent-view/1` for diff --git a/MIGRATION.md b/MIGRATION.md index e401c1629..38a42dc48 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,12 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Rebuild graphs with programmatic framework routes to remove filesystem-derived +containment between independent routers. Framework-pack semantics version 7 +invalidates prior build profiles and build-state seals; disposable framework +facts can be rebuilt from source. Hub rankings, navigation paths, and communities +can change. Existing historical realizations remain unchanged. + Rebuild existing Go graphs to receive the control-initializer and receiver shadowing corrections. Normal builds automatically discard AST cache versions older than 4. Source-proven calls can appear and incorrectly attributed calls diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 7295d9238..9ef8984f5 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -253,6 +253,15 @@ not an addition to the original held-out score. Run it with the same The first frozen results and post-output review are recorded in `heldout_panel_a_review.json` and the main code-graph intelligence audit report. + +`suite_mcp_panel_a.json` extends the same frozen panel graphs to 60 MCP requests +after observing the CLI results. It is a development extension, with questions +committed before MCP execution. `mcp_panel_a_review.json` records graph-consistency +and payload results, and `hub_role_reviews_panel_a.json` records the complete +post-output source-role census. Neither establishes functional community quality +or god-object defects. `edge_witnesses_chi_route_hierarchy_diagnostic.json` +separately records an unsupported Compass containment edge discovered through +the hub review; missing Graphify route identities cannot pass that negative. Keep the original Click edge witness: its missing second `_wrap_io_open` site is corrected only in `edge_witnesses_heldout_click_corrected.json`. Report both registered and corrected diagnostic scores. The path auditor retains nonzero, diff --git a/benchmarks/agent_query/edge_witnesses_chi_route_hierarchy_diagnostic.json b/benchmarks/agent_query/edge_witnesses_chi_route_hierarchy_diagnostic.json new file mode 100644 index 000000000..136cfa5bf --- /dev/null +++ b/benchmarks/agent_query/edge_witnesses_chi_route_hierarchy_diagnostic.json @@ -0,0 +1,27 @@ +{ + "schema": "compass.agent-edge-witnesses/1", + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "scope": "Post-output negative discovered by reviewing a high-degree Compass route hub. Independent test-local chi.NewRouter instances do not create route containment across their files. Graphify absence of route endpoint identities is unavailable evidence, not a correct negative. This is not a representative precision sample.", + "witnesses": [ + { + "id": "independent-chi-test-routers-do-not-contain-each-other", + "source": { + "file": "middleware/clean_path_test.go", + "line": 14, + "symbol": "GET /users/1", + "text": "r.Get(\"/users/1\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }))" + }, + "target": { + "file": "middleware/throttle_test.go", + "line": 22, + "symbol": "GET /", + "text": "r.Get(\"/\", func(w http.ResponseWriter, r *http.Request) {" + }, + "relation": "contains", + "expected": "absent", + "occurrences": [], + "judgment": "TestCleanPath creates its local r with chi.NewRouter at clean_path_test.go:12; TestThrottleBacklog independently creates its r at throttle_test.go:18. Neither registers or mounts the other. Shared receiver spelling r and directory membership provide no parent-route evidence." + } + ] +} diff --git a/benchmarks/agent_query/go_receiver_development_review.json b/benchmarks/agent_query/go_receiver_development_review.json index 60538dd8e..45fd1d1a7 100644 --- a/benchmarks/agent_query/go_receiver_development_review.json +++ b/benchmarks/agent_query/go_receiver_development_review.json @@ -2,12 +2,15 @@ "artifactDigests": { "go-callback-regression-after-01.log": "8f49025c2134d0a0b880b93d054fd27af10b56d0268dc16243685eeae0760d57", "go-callback-regression-before-01.log": "4ecb63cc44c9397e0273a2f3fdc2ab1d14151d3bd16cd795938d52d91056cab2", + "go-initializer-build-02.log": "a8df3a82dbb578d6a6c88e7ac7090dc61cf40e2797616e259e019dfc06605872", "go-initializer-chi-diagnostic-edge-audit-01.json": "35b94fe0687072106320ebfde1b4b712fd891742ddb5f1a7b0bc6eb2d93eb79c", "go-initializer-chi-diagnostic-edge-audit-02.json": "2f8963363b492572d9af86929786ef8391d35a388d6fd170804021a77a111af6", "go-initializer-clippy-02.log": "d423b7f39f17cadab6fc174d5e9b69412b0dec968efb6d2a8847f8a7d9045911", "go-initializer-final-delta-01.json": "9ee59cbc29c017e28a1d2f3cd8524afdf5fe004d8756ac179535d3a8c2d782c2", "go-initializer-native-03.log": "dcb437d6aa7fdadd3e7377d481532181a6634ecbc990b8940210e9cca468e360", "go-initializer-python-02.log": "c8d67e5826b21f7eef14a1c4f2199f6031e77a8ddb61861be33cf790942a5037", + "go-initializer-qualification-01.log": "c17480a3ce59427432fba08c7368951a8cc284a65a0d44a24fd410e037f7048e", + "go-initializer-qualification-02.log": "632995fdcff528cef061312067b2e7cf3f344dedd2b95b771559404648c44887", "go_initializer_delta.py": "6f1642fafa950051375382d86615b620c23e582cf9d8b70c8a8e33197e54d164" }, "diagnosticResult": { @@ -536,6 +539,6 @@ "python": { "passed": 95 }, - "qualification": "running; do not infer a full gate pass from partial logs" + "qualification": "Both fixtures-only invocations completed with exit 0; qualification-02 started after the final Go production edit. Includes semantic/topology/Markdown and React fixture qualification. These gates do not cover the subsequently discovered programmatic route-hierarchy false positive." } } diff --git a/benchmarks/agent_query/hub_role_reviews_panel_a.json b/benchmarks/agent_query/hub_role_reviews_panel_a.json new file mode 100644 index 000000000..e694c854a --- /dev/null +++ b/benchmarks/agent_query/hub_role_reviews_panel_a.json @@ -0,0 +1,1310 @@ +{ + "schema": "compass.hub-source-review/1", + "scope": "Post-output manual declaration-role census of all fifty returned MCP hubs per tool on panel A. Unique graph identities and source anchors are checked, but roles do not establish responsibility count, source-edge precision, or god-object defects. Ambiguous display identities stay unknown. Different returned sets are not a shared precision denominator.", + "roles": { + "type": "Production type declaration, including class, struct, alias, interface, and public testing API types.", + "callable": "Production function or method.", + "test-helper": "Callable used by repository tests.", + "test-type": "Class or structured helper type in repository tests.", + "source-module": "Whole source-file module container.", + "test-module": "Whole test-file module container.", + "example-callable": "Function in an example application.", + "benchmark-callable": "Benchmark support function.", + "generic-implementation": "Implementation block whose target is its own generic parameter.", + "trait-implementation": "Trait implementation block, not a new type declaration.", + "test-route": "Framework route registration in a repository test; not a callable/type declaration.", + "example-route": "Framework route registration in an example application; not a callable/type declaration.", + "example-type": "Type in a demonstration CLI/application.", + "documentation-tooling": "Callable used to build or transform repository documentation." + }, + "inputRunSha256": "af1f84bee444ea3ab05e2db839562e7e4b392f5f1b1da8cba7e91fca5aa5dd28", + "sources": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassGraphSha256": "e7a5dcb40973eadb8281416e125ef0b78a6330b74e0361ac52dcb083ba77204a", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassGraphSha256": "8035487618e4e96af8b7cc668d92eaea9ee3ec2a831c486d20055bc808fa23f6", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "reviews": [ + { + "repository": "chi", + "tool": "compass", + "rank": 1, + "label": "Mux", + "degree": 71, + "role": "type", + "id": "sha256:d003705234e34bbb357969b1c76ccd4cd1adadcf314b380d8ed89255dd633335", + "file": "mux.go", + "line": 21, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "type Mux struct {\n\t// The computed mux handler made of the chained middleware stack and\n\t// the tree router\n\thandler http.Handler" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 2, + "label": "GET /users/1", + "degree": 59, + "role": "test-route", + "id": "sha256:0cf4d5e0e8701ef22a2d876c9698ded7531243a31cd62fe774ec18d23a5d0971", + "file": "middleware/clean_path_test.go", + "line": 14, + "sourceFileSha256": "81b6a3dff384a06104eefc1a9f5aea74394d36745ef78c4460cc80b56151bb00", + "sourceText": "\tr.Get(\"/users/1\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }))\n\n\tw := httptest.NewRecorder()\n\treq, _ := http.NewRequest(\"GET\", \"/users////1\", nil)" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 3, + "label": "NewRouter()", + "degree": 51, + "role": "callable", + "id": "sha256:1928266631ebdc4651cac18aa5abc7ab261abc74a669ec9071a53d68b1f30699", + "file": "chi.go", + "line": 62, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "func NewRouter() *Mux {\n\treturn NewMux()\n}\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 4, + "label": "Router", + "degree": 49, + "role": "type", + "id": "sha256:8ab5bf62c8dcdea65c3d185e73de6efa0660677862d5b997ed6086d6b3571fae", + "file": "chi.go", + "line": 68, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "type Router interface {\n\thttp.Handler\n\tRoutes\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 5, + "label": "node", + "degree": 34, + "role": "type", + "id": "sha256:a5f10ee47ecf99eb3039567ca90b2070ee96c4e323ea1c7baf0f3b91b5febaf9", + "file": "tree.go", + "line": 97, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "sourceText": "type node struct {\n\t// subroutes on the leaf node\n\tsubroutes Routes\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 6, + "label": "testRequest()", + "degree": 31, + "role": "test-helper", + "id": "sha256:affecca8ae0fa3330373f2b674191af88d18c533d78f967e8dc4e7158a2a5f13", + "file": "mux_test.go", + "line": 2075, + "sourceFileSha256": "162d82fa10418b99baf750e3fbcb1fee018bfd654850a9f01382a175390dc965", + "sourceText": "func testRequest(t *testing.T, ts *httptest.Server, method, path string, body io.Reader) (*http.Response, string) {\n\treq, err := http.NewRequest(method, ts.URL+path, body)\n\tif err != nil {\n\t\tt.Fatal(err)" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 7, + "label": "basicWriter", + "degree": 24, + "role": "type", + "id": "sha256:93b2e10be45a493f2b1da664d2d0e221b054df3bf9b5165f30bf13d402e37716", + "file": "middleware/wrap_writer.go", + "line": 74, + "sourceFileSha256": "7b6ed24d3d5bfd362e00851d117e74273b02597a1d9e9eb243cbf7aae22597c8", + "sourceText": "type basicWriter struct {\n\thttp.ResponseWriter\n\ttee io.Writer\n\tcode int" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 8, + "label": ".handle()", + "degree": 22, + "role": "callable", + "id": "sha256:f347f98bee807aa7b86ad9bb7962bce9fffcacb0e94be392f6bca77e9d6c978e", + "file": "mux.go", + "line": 430, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node {\n\tif len(pattern) == 0 || pattern[0] != '/' {\n\t\tpanic(fmt.Sprintf(\"chi: routing pattern must begin with '/' in '%s'\", pattern))\n\t}" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 9, + "label": "Context", + "degree": 21, + "role": "type", + "id": "sha256:3c0e2dbc07035a3a8f70ae8bae0699c3db0914711df3e9c57fbe21b7bc4ea06b", + "file": "context.go", + "line": 45, + "sourceFileSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "sourceText": "type Context struct {\n\tRoutes Routes\n\n\t// parentCtx is the parent of this one, for using Context as a" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 10, + "label": "GET /", + "degree": 20, + "role": "example-route", + "id": "sha256:6f74f6829c1ece47600b30691db107ba606954626ded041cc3e331aa1786f96e", + "file": "_examples/todos-resource/main.go", + "line": 22, + "sourceFileSha256": "a0a551b6d6ca81210b129cd2a70d76e38370b2461be78813e6f117cfef6da020", + "sourceText": "\tr.Get(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Write([]byte(\".\"))\n\t})\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 1, + "label": "NewRouter()", + "degree": 51, + "role": "callable", + "id": "chi_newrouter", + "file": "chi.go", + "line": 62, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "func NewRouter() *Mux {\n\treturn NewMux()\n}\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 2, + "label": "Mux", + "degree": 40, + "role": "type", + "id": "mux_go_chi_mux", + "file": "mux.go", + "line": 21, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "type Mux struct {\n\t// The computed mux handler made of the chained middleware stack and\n\t// the tree router\n\thandler http.Handler" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 3, + "label": "testRequest()", + "degree": 35, + "role": null + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 4, + "label": "Context", + "degree": 30, + "role": "type", + "id": "chi_context", + "file": "context.go", + "line": 45, + "sourceFileSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "sourceText": "type Context struct {\n\tRoutes Routes\n\n\t// parentCtx is the parent of this one, for using Context as a" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 5, + "label": "Router", + "degree": 28, + "role": "type", + "id": "chi_go_chi_router", + "file": "chi.go", + "line": 68, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "type Router interface {\n\thttp.Handler\n\tRoutes\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 6, + "label": "node", + "degree": 22, + "role": "type", + "id": "chi_node", + "file": "tree.go", + "line": 97, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "sourceText": "type node struct {\n\t// subroutes on the leaf node\n\tsubroutes Routes\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 7, + "label": "run()", + "degree": 18, + "role": "test-helper", + "id": "middleware_client_ip_test_run", + "file": "middleware/client_ip_test.go", + "line": 706, + "sourceFileSha256": "bfc0d690534b833f73117d8a4c25aaedef6923c3145b6aadd3a0c149e2a427de", + "sourceText": "func run(t *testing.T, mw func(http.Handler) http.Handler, buildReq func(*http.Request)) string {\n\tt.Helper()\n\treq := httptest.NewRequest(\"GET\", \"/\", nil)\n\tbuildReq(req)" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 8, + "label": "basicWriter", + "degree": 17, + "role": "type", + "id": "middleware_basicwriter", + "file": "middleware/wrap_writer.go", + "line": 74, + "sourceFileSha256": "7b6ed24d3d5bfd362e00851d117e74273b02597a1d9e9eb243cbf7aae22597c8", + "sourceText": "type basicWriter struct {\n\thttp.ResponseWriter\n\ttee io.Writer\n\tcode int" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 9, + "label": "ClientIPFromXFF()", + "degree": 16, + "role": "callable", + "id": "middleware_client_ip_clientipfromxff", + "file": "middleware/client_ip.go", + "line": 93, + "sourceFileSha256": "fc4eef97991796067607038437bd3b23e04337ba9b07ed3c4147d8b49ecf5e9e", + "sourceText": "func ClientIPFromXFF(trustedIPPrefixes ...string) func(http.Handler) http.Handler {\n\tprefixes := make([]netip.Prefix, len(trustedIPPrefixes))\n\tfor i, p := range trustedIPPrefixes {\n\t\tprefixes[i] = netip.MustParsePrefix(p)" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 10, + "label": "assertEqual()", + "degree": 15, + "role": "test-helper", + "id": "middleware_middleware_test_assertequal", + "file": "middleware/middleware_test.go", + "line": 208, + "sourceFileSha256": "f93aec66e191433ab67ea727d45dc6cbdb4184d874132339c2715431c41e3eba", + "sourceText": "func assertEqual(t *testing.T, a, b any) {\n\tt.Helper()\n\tif !reflect.DeepEqual(a, b) {\n\t\tt.Fatalf(\"expecting values to be equal but got: '%v' and '%v'\", a, b)" + }, + { + "repository": "click", + "tool": "compass", + "rank": 1, + "label": "Context", + "degree": 303, + "role": "type", + "id": "sha256:04db8174ad36dd65699520c296fd738ff4258d7e4c6ac31d8c5c48d084032ded", + "file": "src/click/core.py", + "line": 234, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Context:\n \"\"\"The context is a special internal object that holds state relevant\n for the script execution at every single level. It's normally invisible\n to commands unless they opt-in to getting access to it." + }, + { + "repository": "click", + "tool": "compass", + "rank": 2, + "label": "Parameter", + "degree": 129, + "role": "type", + "id": "sha256:ae1ae71b96a9620030b4830d9f4e3159e6de8089f940e0d4bd1f04e740d93062", + "file": "src/click/core.py", + "line": 2237, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Parameter(ABC):\n r\"\"\"A parameter to a command comes in two versions: they are either\n :class:`Option`\\s or :class:`Argument`\\s. Other subclasses are currently\n not supported by design as some of the internals for parsing are" + }, + { + "repository": "click", + "tool": "compass", + "rank": 3, + "label": "Command", + "degree": 85, + "role": "type", + "id": "sha256:e822a51cf5afc73cfb7f3fccc077fd04bcb61da80b77998f22fb4b663ac4ea66", + "file": "src/click/core.py", + "line": 985, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Command:\n \"\"\"Commands are the basic building block of command line interfaces in\n Click. A basic command handles command line parsing and might dispatch\n more parsing to commands nested below it." + }, + { + "repository": "click", + "tool": "compass", + "rank": 4, + "label": "Option", + "degree": 51, + "role": "type", + "id": "sha256:6157d30f9a6fd24baf7d621b601792eb67eb2d7b9d3e185f7a44d30dee887d5b", + "file": "src/click/core.py", + "line": 2963, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Option(Parameter):\n \"\"\"Options are usually optional values on the command line and\n have some extra features that arguments don't have.\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 5, + "label": "Group", + "degree": 49, + "role": "type", + "id": "sha256:15b0848405f27a8502a9efc8d6139bf542482cedc1eacb6becaddfe554f3df0f", + "file": "src/click/core.py", + "line": 1699, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Group(Command):\n \"\"\"A group is a command that nests other commands (or more groups).\n\n :param name: The name of the group command." + }, + { + "repository": "click", + "tool": "compass", + "rank": 6, + "label": "ParamType", + "degree": 40, + "role": "type", + "id": "sha256:1f00115a5f86bc3b6514dbac1daa253d8cfaafa43b58b4f6cea5ccac733d678b", + "file": "src/click/types.py", + "line": 65, + "sourceFileSha256": "30a39185da37bbd98ef47153f7bfd310b04eac44bd9b9b2fe15998406337757e", + "sourceText": "class ParamType(t.Generic[_ValueT_co, _InputT_contra], abc.ABC):\n \"\"\"Represents the type of a parameter. Validates and converts values\n from the command line or Python into the correct type.\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 7, + "label": "HelpFormatter", + "degree": 37, + "role": "type", + "id": "sha256:a2a7bb3dd442be664b82216a6b3acd8a1e8079c2abb02f67fc7455da68e72648", + "file": "src/click/formatting.py", + "line": 110, + "sourceFileSha256": "f125b628692f8dfcfd43535b7a88cc1ee64137471f9d0243b389aa0cfea85e6b", + "sourceText": "class HelpFormatter:\n \"\"\"This class helps with formatting text-based help pages. It's\n usually just needed for very special internal cases, but it's also\n exposed so that developers can write their own fancy outputs." + }, + { + "repository": "click", + "tool": "compass", + "rank": 8, + "label": "echo()", + "degree": 36, + "role": "callable", + "id": "sha256:abda0c42f0c49427afee855d0256edc7b5b579da8e43dbcf33162ee7992d3c8a", + "file": "src/click/utils.py", + "line": 240, + "sourceFileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17", + "sourceText": "def echo(\n message: object = None,\n file: t.IO[t.Any] | None = None,\n nl: bool = True," + }, + { + "repository": "click", + "tool": "compass", + "rank": 9, + "label": "_get_words()", + "degree": 26, + "role": "test-helper", + "id": "sha256:dee697366b1c46d71686a3eccb36489b42833ef72a15c620ad3f874059c3e733", + "file": "tests/test_shell_completion.py", + "line": 29, + "sourceFileSha256": "d45f0b1b42850463a3a07273989518d477f8b19c4ca0d8ef74dc29c309276bd8", + "sourceText": "def _get_words(cli, args, incomplete):\n return [c.value for c in _get_completions(cli, args, incomplete)]\n\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 10, + "label": "Argument", + "degree": 25, + "role": "type", + "id": "sha256:23ddee6a475e892ac17dd6dee048b0da67616a649067b5918fab30b499796578", + "file": "src/click/core.py", + "line": 3800, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Argument(Parameter):\n \"\"\"Arguments are positional parameters to a command. They generally\n provide fewer features than options but can have infinite ``nargs``\n and are required by default." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 1, + "label": "Context", + "degree": 174, + "role": "type", + "id": "src_click_core_context", + "file": "src/click/core.py", + "line": 234, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Context:\n \"\"\"The context is a special internal object that holds state relevant\n for the script execution at every single level. It's normally invisible\n to commands unless they opt-in to getting access to it." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 2, + "label": "Command", + "degree": 100, + "role": "type", + "id": "src_click_core_command", + "file": "src/click/core.py", + "line": 985, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Command:\n \"\"\"Commands are the basic building block of command line interfaces in\n Click. A basic command handles command line parsing and might dispatch\n more parsing to commands nested below it." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 3, + "label": "Parameter", + "degree": 91, + "role": "type", + "id": "src_click_core_parameter", + "file": "src/click/core.py", + "line": 2237, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Parameter(ABC):\n r\"\"\"A parameter to a command comes in two versions: they are either\n :class:`Option`\\s or :class:`Argument`\\s. Other subclasses are currently\n not supported by design as some of the internals for parsing are" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 4, + "label": "CliRunner", + "degree": 65, + "role": "type", + "id": "src_click_testing_clirunner", + "file": "src/click/testing.py", + "line": 317, + "sourceFileSha256": "76f4e649bc53000d667c8c0deff1591970fa034b0f085a207165839b2301019a", + "sourceText": "class CliRunner:\n \"\"\"The CLI runner provides functionality to invoke a Click command line\n script for unittesting purposes in a isolated environment. This only\n works in single-threaded systems without any concurrency as it changes the" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 5, + "label": "Option", + "degree": 55, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 6, + "label": "cmd()", + "degree": 45, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 7, + "label": "Group", + "degree": 44, + "role": "type", + "id": "src_click_core_group", + "file": "src/click/core.py", + "line": 1699, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Group(Command):\n \"\"\"A group is a command that nests other commands (or more groups).\n\n :param name: The name of the group command." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 8, + "label": "option()", + "degree": 37, + "role": "callable", + "id": "src_click_decorators_option", + "file": "src/click/decorators.py", + "line": 352, + "sourceFileSha256": "16069357615691fcdfc9c794bb515f6009eb35aad6f7a76c017bdce06dae8d55", + "sourceText": "def option(\n *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any\n) -> t.Callable[[FC], FC]:\n \"\"\"Attaches an option to the command. All positional arguments are" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 9, + "label": "echo()", + "degree": 37, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 10, + "label": "Choice", + "degree": 31, + "role": "type", + "id": "src_click_types_choice", + "file": "src/click/types.py", + "line": 342, + "sourceFileSha256": "30a39185da37bbd98ef47153f7bfd310b04eac44bd9b9b2fe15998406337757e", + "sourceText": "class Choice(ParamType[_ValueT_co], t.Generic[_ValueT_co]):\n \"\"\"The choice type allows a value to be checked against a fixed set\n of supported values.\n" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 1, + "label": ".parse()", + "degree": 798, + "role": "callable", + "id": "sha256:f7d4781fbbe1422803605dbf458fa8a1db80c4d645eb71314550f10d633b9866", + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 77, + "sourceFileSha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7", + "sourceText": " public static Document parse(String html) {\n return Parser.parse(html, \"\");\n }\n" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 2, + "label": "Element", + "degree": 635, + "role": "type", + "id": "sha256:95c73544da32be6d2e83b3eebff33a5c4e0119744fa6da3e4957a661dfa20956", + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 50, + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "sourceText": "public class Element extends Node implements Iterable {\n private static final List EmptyChildren = Collections.emptyList();\n private static final NodeList EmptyNodeList = new NodeList(0);\n static final String BaseUriKey = Attributes.internalKey(\"baseUri\");" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 3, + "label": "Node", + "degree": 295, + "role": "type", + "id": "sha256:cb5d1a2ae3e71d4c6956fc589767e1bfc7f1b6a9d47417ca5acf37308c46bd2c", + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 26, + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "sourceText": "public abstract class Node implements Cloneable {\n static final List EmptyNodes = Collections.emptyList();\n static final String EmptyString = \"\";\n @Nullable Element parentNode; // Nodes don't always have parents" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 4, + "label": "HtmlParserTest", + "degree": 284, + "role": "test-type", + "id": "sha256:de2de457e5558493b588e65ee2aa2d3cb6ffbce7cefe413a70ad228e62f72f85", + "file": "src/test/java/org/jsoup/parser/HtmlParserTest.java", + "line": 32, + "sourceFileSha256": "859f7ccb7c09558e54328e0eb6001f8fcc5b84e67c71b0bd2533ffe070495719", + "sourceText": "public class HtmlParserTest {\n\n @Test public void parsesSimpleDocument() {\n String html = \"First!

First post!

\";" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 5, + "label": "ElementTest", + "degree": 261, + "role": "test-type", + "id": "sha256:41d1bdb7999a0b8d28dde83783ce8473f14b015b88cdebaf451f2d965f60b9c9", + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 46, + "sourceFileSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc", + "sourceText": "public class ElementTest {\n private final String reference = \"

Hello

Another element

\";\n\n private static void validateScriptContents(String src, Element el) {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 6, + "label": "CharacterReader", + "degree": 206, + "role": "type", + "id": "sha256:8dbd9566f2d7185705a924a0e685666f5b58349f89d04c2d7f7c1f2ade5250f1", + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 22, + "sourceFileSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c", + "sourceText": "public final class CharacterReader implements AutoCloseable {\n static final char EOF = (char) -1;\n private static final int MaxStringCacheLen = 12;\n private static final int StringCacheSize = 512;" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 7, + "label": "HtmlTreeBuilder", + "degree": 203, + "role": "type", + "id": "sha256:448d5953d9c1e7f7d0129715551893b4a930bca8a120832d6895ab762e0f9337", + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 33, + "sourceFileSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f", + "sourceText": "public class HtmlTreeBuilder extends TreeBuilder {\n static final String[] TagMathMlTextIntegration = new String[]{\"mi\", \"mn\", \"mo\", \"ms\", \"mtext\"};\n static final String[] TagSvgHtmlIntegration = new String[]{\"desc\", \"foreignObject\", \"title\"};\n static final String[] TagFormListed = {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 8, + "label": ".body()", + "degree": 187, + "role": "callable", + "id": "sha256:f4f51f52089ad7a2a597230a89bbae7dd488d3e3a2f4d872df44b6325e6aa1d8", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 155, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": " public Element body() {\n final Element html = htmlEl();\n Element el = html.firstElementChild();\n while (el != null) {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 9, + "label": "Document", + "degree": 182, + "role": "type", + "id": "sha256:f947ec4bb8822b5d9607c6786bb89ebe182f9c5d31ccf7b829fa583f5fa9bd82", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 26, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": "public class Document extends Element {\n private @Nullable Connection connection; // the connection this doc was fetched from, if any\n private OutputSettings outputSettings = new OutputSettings();\n private Parser parser; // the parser used to parse this document" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 10, + "label": ".stripNewlines()", + "degree": 177, + "role": "test-helper", + "id": "sha256:5517e1de0e519d02805292870167f5e1176438f58f9336b90139ee55cf9c59f4", + "file": "src/test/java/org/jsoup/TextUtil.java", + "line": 16, + "sourceFileSha256": "48216be48aa35dae2459fc3f2ea487abda9e3a6924dae3c0b661a4fd6cc684d2", + "sourceText": " public static String stripNewlines(String text) {\n return stripper.matcher(text).replaceAll(\"\");\n }\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 1, + "label": "Element", + "degree": 415, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_element_element", + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 50, + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "sourceText": "public class Element extends Node implements Iterable {\n private static final List EmptyChildren = Collections.emptyList();\n private static final NodeList EmptyNodeList = new NodeList(0);\n static final String BaseUriKey = Attributes.internalKey(\"baseUri\");" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 2, + "label": "HtmlParserTest", + "degree": 284, + "role": "test-type", + "id": "src_test_java_org_jsoup_parser_htmlparsertest_htmlparsertest", + "file": "src/test/java/org/jsoup/parser/HtmlParserTest.java", + "line": 32, + "sourceFileSha256": "859f7ccb7c09558e54328e0eb6001f8fcc5b84e67c71b0bd2533ffe070495719", + "sourceText": "public class HtmlParserTest {\n\n @Test public void parsesSimpleDocument() {\n String html = \"First!

First post!

\";" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 3, + "label": "ElementTest", + "degree": 260, + "role": "test-type", + "id": "src_test_java_org_jsoup_nodes_elementtest_elementtest", + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 46, + "sourceFileSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc", + "sourceText": "public class ElementTest {\n private final String reference = \"

Hello

Another element

\";\n\n private static void validateScriptContents(String src, Element el) {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 4, + "label": "Node", + "degree": 234, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_node_node", + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 26, + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "sourceText": "public abstract class Node implements Cloneable {\n static final List EmptyNodes = Collections.emptyList();\n static final String EmptyString = \"\";\n @Nullable Element parentNode; // Nodes don't always have parents" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 5, + "label": "HtmlTreeBuilder", + "degree": 162, + "role": "type", + "id": "src_main_java_org_jsoup_parser_htmltreebuilder_htmltreebuilder", + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 33, + "sourceFileSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f", + "sourceText": "public class HtmlTreeBuilder extends TreeBuilder {\n static final String[] TagMathMlTextIntegration = new String[]{\"mi\", \"mn\", \"mo\", \"ms\", \"mtext\"};\n static final String[] TagSvgHtmlIntegration = new String[]{\"desc\", \"foreignObject\", \"title\"};\n static final String[] TagFormListed = {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 6, + "label": "SelectorTest", + "degree": 160, + "role": "test-type", + "id": "src_test_java_org_jsoup_select_selectortest_selectortest", + "file": "src/test/java/org/jsoup/select/SelectorTest.java", + "line": 31, + "sourceFileSha256": "0560f38b89cb82e8f7e12386d468b43bd069874941be5fa85ad3227363e18d66", + "sourceText": "public class SelectorTest {\n\n /** Test that the selected elements match exactly the specified IDs. */\n public static void assertSelectedIds(Elements els, String... ids) {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 7, + "label": "CharacterReader", + "degree": 139, + "role": "type", + "id": "src_main_java_org_jsoup_parser_characterreader_characterreader", + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 22, + "sourceFileSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c", + "sourceText": "public final class CharacterReader implements AutoCloseable {\n static final char EOF = (char) -1;\n private static final int MaxStringCacheLen = 12;\n private static final int StringCacheSize = 512;" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 8, + "label": "Evaluator", + "degree": 134, + "role": "type", + "id": "src_main_java_org_jsoup_select_evaluator_evaluator", + "file": "src/main/java/org/jsoup/select/Evaluator.java", + "line": 31, + "sourceFileSha256": "5a3ce0742ddf5d5bb647e8627735c47c656f5642361f41fbb30f26fa7b6f7e8d", + "sourceText": "public abstract class Evaluator {\n protected Evaluator() {\n }\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 9, + "label": "Tokeniser", + "degree": 122, + "role": "type", + "id": "src_main_java_org_jsoup_parser_tokeniser_tokeniser", + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "line": 16, + "sourceFileSha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f", + "sourceText": "final class Tokeniser {\n static final char replacementChar = '\\uFFFD'; // replaces null character\n private static final char[] notCharRefCharsSorted = new char[]{'\\t', '\\n', '\\r', '\\f', ' ', '<', '&'};\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 10, + "label": "Document", + "degree": 121, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_document_document", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 26, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": "public class Document extends Element {\n private @Nullable Connection connection; // the connection this doc was fetched from, if any\n private OutputSettings outputSettings = new OutputSettings();\n private Parser parser; // the parser used to parse this document" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 1, + "label": "createStore.spec", + "degree": 159, + "role": "test-module", + "id": "sha256:74fe6fa433c85236a87bda7c1d2beb4256849ee0e3e4c16dac078f89c8d7251d", + "file": "test/createStore.spec.ts", + "line": 1, + "sourceFileSha256": "28b82562daeb4dbbce155397db1825c1b47e28cdac317033bd1655619b5eadb6", + "sourceText": "import type { StoreEnhancer, Action, Store, Reducer } from 'redux'\nimport { createStore, combineReducers } from 'redux'\nimport { vi } from 'vitest'\nimport {" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 2, + "label": "combineReducers.spec", + "degree": 84, + "role": "test-module", + "id": "sha256:10a60eda4e3187f3fc3119318562b3e286b7a8903bd64be8946ec37494becf44", + "file": "test/combineReducers.spec.ts", + "line": 1, + "sourceFileSha256": "4dc22297b4938fa7278dde5350870e2fa31c8ff87a7d48b943a3b8fc64ddfb84", + "sourceText": "/* oxlint-disable no-console */\nimport type { Reducer, Action } from 'redux'\nimport {\n createStore," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 3, + "label": "defaultAssembleReplacementNodes()", + "degree": 74, + "role": "documentation-tooling", + "id": "sha256:9695b4120077fb0d683e8f491bae870400b249ec2652a51e73203216ba7b5f60", + "file": "website/plugins/remark-typescript-tools/transpileCodeblocks/plugin.ts", + "line": 215, + "sourceFileSha256": "dd7cbd61846175fcad25c515ffa2c4c95d44ec7b739dd3dcc11e181c59d3c3f7", + "sourceText": "export function defaultAssembleReplacementNodes(\n node: CodeNode,\n file: VFile,\n virtualFolder: string," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 4, + "label": "index", + "degree": 70, + "role": "source-module", + "id": "sha256:dce3d43d2e05a1e2cff376a47a40c65b4c8879f6e24b0f34fb2bc0dd19d219fa", + "file": "src/index.ts", + "line": 1, + "sourceFileSha256": "8bb0a6852611119382cae7dc89ab600d43701c87644e518042ab23134a4c16a9", + "sourceText": "// functions\nimport { createStore, legacy_createStore } from './createStore'\nimport combineReducers from './combineReducers'\nimport bindActionCreators from './bindActionCreators'" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 5, + "label": "reducers.test-d", + "degree": 68, + "role": "test-module", + "id": "sha256:f1fdcad28d45e9456b801123f7cb03ae12f0875f58aa73a52e3abd8a73e0b3f7", + "file": "test/typescript/reducers.test-d.ts", + "line": 1, + "sourceFileSha256": "6e9d92a666fef0546948aa96d7e7d14b96f31c3f660074039d8cc0bb8a1e5d0e", + "sourceText": "import type {\n Action,\n AnyAction,\n PreloadedStateShapeFromReducersMapObject," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 6, + "label": "transpileCodeblocks()", + "degree": 56, + "role": "documentation-tooling", + "id": "sha256:e1a2859c6983979bae899fd5f06a8e0d221aa1811cb1d42787ad0ac98e8067ea", + "file": "website/plugins/remark-typescript-tools/transpileCodeblocks/plugin.ts", + "line": 46, + "sourceFileSha256": "dd7cbd61846175fcad25c515ffa2c4c95d44ec7b739dd3dcc11e181c59d3c3f7", + "sourceText": "export const transpileCodeblocks: Plugin<[TranspileCodeblocksSettings]> =\n function ({\n compilerSettings,\n postProcessTranspiledJs = defaultPostProcessTranspiledJs," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 7, + "label": "enhancers.test-d", + "degree": 47, + "role": "test-module", + "id": "sha256:70cc954750611610bf9773a8c59e07344ac1f8fafc7107565c5c9e346dccbf5b", + "file": "test/typescript/enhancers.test-d.ts", + "line": 1, + "sourceFileSha256": "12fea8393645a50c8be7ee2b1b81839bcb903e80c7394361ba60268e80ad4313", + "sourceText": "import type { Action, Reducer, StoreEnhancer } from 'redux'\nimport { createStore } from 'redux'\n\ninterface State {" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 8, + "label": "store.test-d", + "degree": 35, + "role": "test-module", + "id": "sha256:9bf18a6ffd0f35c3ce4b2e33d8ef1bcd285fc930e09955b98176f15289db3124", + "file": "test/typescript/store.test-d.ts", + "line": 1, + "sourceFileSha256": "ad0d2a2e2490699420a14afc689bd57d7967d5220e41f97c257b86c90098bd77", + "sourceText": "import type {\n Action,\n Observer,\n Reducer," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 9, + "label": "store", + "degree": 33, + "role": "source-module", + "id": "sha256:802d7602df7b8cac096452350c66b4dd52d05861d0dee4b3ba6f86421bdf2db4", + "file": "src/types/store.ts", + "line": 1, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "import type { Action, UnknownAction } from './actions'\nimport type { Reducer } from './reducers'\n// oxlint-disable-next-line typescript/no-unused-vars\nimport _$$observable from '../utils/symbol-observable'" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 10, + "label": "createStore()", + "degree": 33, + "role": "callable", + "id": "sha256:ceae4b39dd8902b6888096a1827ee67422d04d3c19d527d674ec740c3e903785", + "file": "src/createStore.ts", + "line": 86, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceText": "export function createStore<\n S,\n A extends Action,\n Ext extends {} = {}," + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 1, + "label": "Action", + "degree": 23, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 2, + "label": "createStore()", + "degree": 22, + "role": "callable", + "id": "src_createstore_createstore", + "file": "src/createStore.ts", + "line": 86, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceText": "export function createStore<\n S,\n A extends Action,\n Ext extends {} = {}," + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 3, + "label": "compilerOptions", + "degree": 21, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 4, + "label": "compilerOptions", + "degree": 19, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 5, + "label": "compilerOptions", + "degree": 16, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 6, + "label": "scripts", + "degree": 14, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 7, + "label": "Dispatch", + "degree": 13, + "role": "type", + "id": "src_types_store_dispatch", + "file": "src/types/store.ts", + "line": 27, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "export interface Dispatch {\n (action: T, ...extraArgs: any[]): T\n}\n" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 8, + "label": "Reducer", + "degree": 12, + "role": "type", + "id": "src_types_reducers_reducer", + "file": "src/types/reducers.ts", + "line": 30, + "sourceFileSha256": "f79a73490d143561e6071c03d852fc78fe057384a6a3e6ff81b81e887df76bc1", + "sourceText": "export type Reducer<\n S = any,\n A extends Action = UnknownAction,\n PreloadedState = S" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 9, + "label": "combineReducers()", + "degree": 12, + "role": "callable", + "id": "src_combinereducers_combinereducers", + "file": "src/combineReducers.ts", + "line": 123, + "sourceFileSha256": "52f4a8c5561bd9ea2532edd31d3a8958dabb13fcc0d11140d158c84e454ce1dc", + "sourceText": "export default function combineReducers(reducers: {\n [key: string]: Reducer\n}) {\n const reducerKeys = Object.keys(reducers)" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 10, + "label": "Store", + "degree": 11, + "role": "type", + "id": "src_types_store_store", + "file": "src/types/store.ts", + "line": 81, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "export interface Store<\n S = any,\n A extends Action = UnknownAction,\n StateExt extends unknown = unknown" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 1, + "label": "DirEntry", + "degree": 67, + "role": "type", + "id": "sha256:9102d21ed875d3f823b6a2b6c19b5c9ec4ec6d5734b63c544d1ede2b4fb09374", + "file": "src/dent.rs", + "line": 35, + "sourceFileSha256": "ca573f4533370a09851579f5940f7cd9bd121b2f30ec51d29a40afdce984683b", + "sourceText": "pub struct DirEntry {\n /// The path as reported by the [`fs::ReadDir`] iterator (even if it's a\n /// symbolic link).\n ///" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 2, + "label": ".new()", + "degree": 51, + "role": "callable", + "id": "sha256:38839ab8bc007bfdd70a2ed3abfb3a77950fb3d008d621d9cf7c5c9bbc8f8d6a", + "file": "src/lib.rs", + "line": 289, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": " pub fn new>(root: P) -> Self {\n WalkDir {\n opts: WalkDirOptions {\n follow_links: false," + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 3, + "label": ".tmp()", + "degree": 50, + "role": "test-helper", + "id": "sha256:0dbcc8c36333b467c48ece7070677fe49f658cd8454b9fd76a40ed7303319fa4", + "file": "src/tests/util.rs", + "line": 84, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn tmp() -> Dir {\n let dir = TempDir::new().unwrap();\n Dir { dir }\n }" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 4, + "label": ".run_recursive()", + "degree": 50, + "role": "test-helper", + "id": "sha256:caafb698ff937854c3b5f77147d26689bf26777be7dca6bcee83d425593d53f8", + "file": "src/tests/util.rs", + "line": 101, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn run_recursive(&self, it: I) -> RecursiveResults\n where\n I: IntoIterator>,\n {" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 5, + "label": ".path()", + "degree": 42, + "role": "test-helper", + "id": "sha256:76ab83daadb4f80254042a7ee65d7cdda5f0c66fa708172245f102572ca08106", + "file": "src/tests/util.rs", + "line": 90, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn path(&self) -> &Path {\n self.dir.path()\n }\n" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 6, + "label": ".mkdirp()", + "degree": 34, + "role": "test-helper", + "id": "sha256:6984d05046d66fab178a4dcaf88c3814c5030b5bfa795fd5d68a3c14469cb71b", + "file": "src/tests/util.rs", + "line": 117, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn mkdirp>(&self, path: P) {\n let full = self.join(path);\n fs::create_dir_all(&full)\n .map_err(|e| {" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 7, + "label": "Error", + "degree": 31, + "role": "type", + "id": "sha256:a66dc1de7a258eafe92e616e5c94a0107fda891db6f9ca33ca1e10fbac5845c9", + "file": "src/error.rs", + "line": 28, + "sourceFileSha256": "ba58bf6f59d196567435d4b66699a928cc237fc7c8df01dc37ab822509905b7c", + "sourceText": "pub struct Error {\n depth: usize,\n inner: ErrorInner,\n}" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 8, + "label": "WalkDir", + "degree": 31, + "role": "type", + "id": "sha256:b647b5809af96e790d083521ec5b9ee189e2a2446b6a08294da8300d43fc420e", + "file": "src/lib.rs", + "line": 234, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct WalkDir {\n opts: WalkDirOptions,\n root: PathBuf,\n}" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 9, + "label": "Result", + "degree": 27, + "role": "type", + "id": "sha256:b0a84ceb03a1b40af4acbce9ba086ead4d7cfc8679eb1cda58f7024a9899c6be", + "file": "src/lib.rs", + "line": 157, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub type Result = ::std::result::Result;\n\n/// A builder to create an iterator for recursively walking a directory.\n///" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 10, + "label": ".touch()", + "degree": 22, + "role": "test-helper", + "id": "sha256:062bcfac2f5cea7a99a078422476e89eb7977023b3d5c265c534e7a2b0ca6c99", + "file": "src/tests/util.rs", + "line": 128, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn touch>(&self, path: P) {\n let full = self.join(path);\n File::create(&full)\n .map_err(|e| {" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 1, + "label": "DirEntry", + "degree": 39, + "role": "type", + "id": "src_dent_direntry", + "file": "src/dent.rs", + "line": 35, + "sourceFileSha256": "ca573f4533370a09851579f5940f7cd9bd121b2f30ec51d29a40afdce984683b", + "sourceText": "pub struct DirEntry {\n /// The path as reported by the [`fs::ReadDir`] iterator (even if it's a\n /// symbolic link).\n ///" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 2, + "label": "IntoIter", + "degree": 22, + "role": "type", + "id": "src_lib_intoiter", + "file": "src/lib.rs", + "line": 566, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct IntoIter {\n /// Options specified in the builder. Depths, max fds, etc.\n opts: WalkDirOptions,\n /// The start path." + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 3, + "label": "WalkDir", + "degree": 19, + "role": "type", + "id": "src_lib_walkdir", + "file": "src/lib.rs", + "line": 234, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct WalkDir {\n opts: WalkDirOptions,\n root: PathBuf,\n}" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 4, + "label": "WalkDirOptions", + "degree": 12, + "role": "type", + "id": "src_lib_walkdiroptions", + "file": "src/lib.rs", + "line": 239, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "struct WalkDirOptions {\n follow_links: bool,\n follow_root_links: bool,\n max_open: usize," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 5, + "label": "Dir", + "degree": 11, + "role": "test-type", + "id": "src_tests_util_dir", + "file": "src/tests/util.rs", + "line": 78, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": "pub struct Dir {\n dir: TempDir,\n}\n" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 6, + "label": "RecursiveResults", + "degree": 11, + "role": "test-type", + "id": "src_tests_util_recursiveresults", + "file": "src/tests/util.rs", + "line": 23, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": "pub struct RecursiveResults {\n ents: Vec,\n errs: Vec,\n}" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 7, + "label": "DirList", + "degree": 10, + "role": "type", + "id": "src_lib_dirlist", + "file": "src/lib.rs", + "line": 661, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "enum DirList {\n /// An opened handle.\n ///\n /// This includes the depth of the handle itself." + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 8, + "label": "Args", + "degree": 10, + "role": "example-type", + "id": "walkdir_list_main_args", + "file": "walkdir-list/main.rs", + "line": 156, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "struct Args {\n dirs: Vec,\n follow_links: bool,\n min_depth: Option," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 9, + "label": "print_paths_flat()", + "degree": 9, + "role": "example-callable", + "id": "walkdir_list_main_print_paths_flat", + "file": "walkdir-list/main.rs", + "line": 102, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "fn print_paths_flat(\n args: &Args,\n mut stdout: W1,\n mut stderr: W2," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 10, + "label": "print_paths_tree()", + "degree": 9, + "role": "example-callable", + "id": "walkdir_list_main_print_paths_tree", + "file": "walkdir-list/main.rs", + "line": 128, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "fn print_paths_tree(\n args: &Args,\n mut stdout: W1,\n mut stderr: W2," + } + ] +} diff --git a/benchmarks/agent_query/hub_role_reviews_panel_a_after_routes.json b/benchmarks/agent_query/hub_role_reviews_panel_a_after_routes.json new file mode 100644 index 000000000..a9b926d1b --- /dev/null +++ b/benchmarks/agent_query/hub_role_reviews_panel_a_after_routes.json @@ -0,0 +1,1310 @@ +{ + "schema": "compass.hub-source-review/1", + "scope": "Post-correction source-role census. Previously reviewed identities retain their roles only after exact source-anchor, excerpt, and whole-file digest equality. Two newly returned Chi hubs were inspected: compressResponseWriter is a production type and bigMux is a test helper. Different returned sets still do not establish source precision or god-object quality.", + "roles": { + "type": "Production type declaration, including class, struct, alias, interface, and public testing API types.", + "callable": "Production function or method.", + "test-helper": "Callable used by repository tests.", + "test-type": "Class or structured helper type in repository tests.", + "source-module": "Whole source-file module container.", + "test-module": "Whole test-file module container.", + "example-callable": "Function in an example application.", + "benchmark-callable": "Benchmark support function.", + "generic-implementation": "Implementation block whose target is its own generic parameter.", + "trait-implementation": "Trait implementation block, not a new type declaration.", + "test-route": "Framework route registration in a repository test; not a callable/type declaration.", + "example-route": "Framework route registration in an example application; not a callable/type declaration.", + "example-type": "Type in a demonstration CLI/application.", + "documentation-tooling": "Callable used to build or transform repository documentation." + }, + "inputRunSha256": "c001978913423a45102f74cd8f079cf78a12e1b425f40c9451ca8725b416ea91", + "sources": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassGraphSha256": "8035487618e4e96af8b7cc668d92eaea9ee3ec2a831c486d20055bc808fa23f6", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "reviews": [ + { + "repository": "chi", + "tool": "compass", + "rank": 1, + "label": "Mux", + "degree": 71, + "role": "type", + "id": "sha256:d003705234e34bbb357969b1c76ccd4cd1adadcf314b380d8ed89255dd633335", + "file": "mux.go", + "line": 21, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "type Mux struct {\n\t// The computed mux handler made of the chained middleware stack and\n\t// the tree router\n\thandler http.Handler" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 2, + "label": "NewRouter()", + "degree": 51, + "role": "callable", + "id": "sha256:1928266631ebdc4651cac18aa5abc7ab261abc74a669ec9071a53d68b1f30699", + "file": "chi.go", + "line": 62, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "func NewRouter() *Mux {\n\treturn NewMux()\n}\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 3, + "label": "Router", + "degree": 49, + "role": "type", + "id": "sha256:8ab5bf62c8dcdea65c3d185e73de6efa0660677862d5b997ed6086d6b3571fae", + "file": "chi.go", + "line": 68, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "type Router interface {\n\thttp.Handler\n\tRoutes\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 4, + "label": "node", + "degree": 34, + "role": "type", + "id": "sha256:a5f10ee47ecf99eb3039567ca90b2070ee96c4e323ea1c7baf0f3b91b5febaf9", + "file": "tree.go", + "line": 97, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "sourceText": "type node struct {\n\t// subroutes on the leaf node\n\tsubroutes Routes\n" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 5, + "label": "testRequest()", + "degree": 31, + "role": "test-helper", + "id": "sha256:affecca8ae0fa3330373f2b674191af88d18c533d78f967e8dc4e7158a2a5f13", + "file": "mux_test.go", + "line": 2075, + "sourceFileSha256": "162d82fa10418b99baf750e3fbcb1fee018bfd654850a9f01382a175390dc965", + "sourceText": "func testRequest(t *testing.T, ts *httptest.Server, method, path string, body io.Reader) (*http.Response, string) {\n\treq, err := http.NewRequest(method, ts.URL+path, body)\n\tif err != nil {\n\t\tt.Fatal(err)" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 6, + "label": "basicWriter", + "degree": 24, + "role": "type", + "id": "sha256:93b2e10be45a493f2b1da664d2d0e221b054df3bf9b5165f30bf13d402e37716", + "file": "middleware/wrap_writer.go", + "line": 74, + "sourceFileSha256": "7b6ed24d3d5bfd362e00851d117e74273b02597a1d9e9eb243cbf7aae22597c8", + "sourceText": "type basicWriter struct {\n\thttp.ResponseWriter\n\ttee io.Writer\n\tcode int" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 7, + "label": ".handle()", + "degree": 22, + "role": "callable", + "id": "sha256:f347f98bee807aa7b86ad9bb7962bce9fffcacb0e94be392f6bca77e9d6c978e", + "file": "mux.go", + "line": 430, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node {\n\tif len(pattern) == 0 || pattern[0] != '/' {\n\t\tpanic(fmt.Sprintf(\"chi: routing pattern must begin with '/' in '%s'\", pattern))\n\t}" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 8, + "label": "Context", + "degree": 21, + "role": "type", + "id": "sha256:3c0e2dbc07035a3a8f70ae8bae0699c3db0914711df3e9c57fbe21b7bc4ea06b", + "file": "context.go", + "line": 45, + "sourceFileSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "sourceText": "type Context struct {\n\tRoutes Routes\n\n\t// parentCtx is the parent of this one, for using Context as a" + }, + { + "repository": "chi", + "tool": "compass", + "rank": 9, + "label": "compressResponseWriter", + "degree": 19, + "role": "type", + "id": "sha256:6652f72be18cde87987c915525e5d29a01e939bbe4845b2bcf212846d4c15447", + "file": "middleware/compress.go", + "line": 281, + "sourceFileSha256": "1a54e1cd05a913dd26bc00faab499cddc58a00646a92af5051961100d1532698", + "sourceText": "type compressResponseWriter struct {\n\thttp.ResponseWriter\n\n\t// The streaming encoder writer to be used if there is one. Otherwise," + }, + { + "repository": "chi", + "tool": "compass", + "rank": 10, + "label": "bigMux()", + "degree": 18, + "role": "test-helper", + "id": "sha256:b52814396c5f93d2f2cf1bd9b4c224234fe1e7d72cb07ec893525963eb8a821b", + "file": "mux_test.go", + "line": 960, + "sourceFileSha256": "162d82fa10418b99baf750e3fbcb1fee018bfd654850a9f01382a175390dc965", + "sourceText": "func bigMux() Router {\n\tvar r *Mux\n\tvar sr3 *Mux\n\t// var sr1, sr2, sr3, sr4, sr5, sr6 *Mux" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 1, + "label": "NewRouter()", + "degree": 51, + "role": "callable", + "id": "chi_newrouter", + "file": "chi.go", + "line": 62, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "func NewRouter() *Mux {\n\treturn NewMux()\n}\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 2, + "label": "Mux", + "degree": 40, + "role": "type", + "id": "mux_go_chi_mux", + "file": "mux.go", + "line": 21, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceText": "type Mux struct {\n\t// The computed mux handler made of the chained middleware stack and\n\t// the tree router\n\thandler http.Handler" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 3, + "label": "testRequest()", + "degree": 35, + "role": null + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 4, + "label": "Context", + "degree": 30, + "role": "type", + "id": "chi_context", + "file": "context.go", + "line": 45, + "sourceFileSha256": "b19edcca252e2fe74e82802c4c7ce1a1c0855728f9ede1288f0224283dfc7e53", + "sourceText": "type Context struct {\n\tRoutes Routes\n\n\t// parentCtx is the parent of this one, for using Context as a" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 5, + "label": "Router", + "degree": 28, + "role": "type", + "id": "chi_go_chi_router", + "file": "chi.go", + "line": 68, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "sourceText": "type Router interface {\n\thttp.Handler\n\tRoutes\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 6, + "label": "node", + "degree": 22, + "role": "type", + "id": "chi_node", + "file": "tree.go", + "line": 97, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "sourceText": "type node struct {\n\t// subroutes on the leaf node\n\tsubroutes Routes\n" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 7, + "label": "run()", + "degree": 18, + "role": "test-helper", + "id": "middleware_client_ip_test_run", + "file": "middleware/client_ip_test.go", + "line": 706, + "sourceFileSha256": "bfc0d690534b833f73117d8a4c25aaedef6923c3145b6aadd3a0c149e2a427de", + "sourceText": "func run(t *testing.T, mw func(http.Handler) http.Handler, buildReq func(*http.Request)) string {\n\tt.Helper()\n\treq := httptest.NewRequest(\"GET\", \"/\", nil)\n\tbuildReq(req)" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 8, + "label": "basicWriter", + "degree": 17, + "role": "type", + "id": "middleware_basicwriter", + "file": "middleware/wrap_writer.go", + "line": 74, + "sourceFileSha256": "7b6ed24d3d5bfd362e00851d117e74273b02597a1d9e9eb243cbf7aae22597c8", + "sourceText": "type basicWriter struct {\n\thttp.ResponseWriter\n\ttee io.Writer\n\tcode int" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 9, + "label": "ClientIPFromXFF()", + "degree": 16, + "role": "callable", + "id": "middleware_client_ip_clientipfromxff", + "file": "middleware/client_ip.go", + "line": 93, + "sourceFileSha256": "fc4eef97991796067607038437bd3b23e04337ba9b07ed3c4147d8b49ecf5e9e", + "sourceText": "func ClientIPFromXFF(trustedIPPrefixes ...string) func(http.Handler) http.Handler {\n\tprefixes := make([]netip.Prefix, len(trustedIPPrefixes))\n\tfor i, p := range trustedIPPrefixes {\n\t\tprefixes[i] = netip.MustParsePrefix(p)" + }, + { + "repository": "chi", + "tool": "graphify", + "rank": 10, + "label": "assertEqual()", + "degree": 15, + "role": "test-helper", + "id": "middleware_middleware_test_assertequal", + "file": "middleware/middleware_test.go", + "line": 208, + "sourceFileSha256": "f93aec66e191433ab67ea727d45dc6cbdb4184d874132339c2715431c41e3eba", + "sourceText": "func assertEqual(t *testing.T, a, b any) {\n\tt.Helper()\n\tif !reflect.DeepEqual(a, b) {\n\t\tt.Fatalf(\"expecting values to be equal but got: '%v' and '%v'\", a, b)" + }, + { + "repository": "click", + "tool": "compass", + "rank": 1, + "label": "Context", + "degree": 303, + "role": "type", + "id": "sha256:04db8174ad36dd65699520c296fd738ff4258d7e4c6ac31d8c5c48d084032ded", + "file": "src/click/core.py", + "line": 234, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Context:\n \"\"\"The context is a special internal object that holds state relevant\n for the script execution at every single level. It's normally invisible\n to commands unless they opt-in to getting access to it." + }, + { + "repository": "click", + "tool": "compass", + "rank": 2, + "label": "Parameter", + "degree": 129, + "role": "type", + "id": "sha256:ae1ae71b96a9620030b4830d9f4e3159e6de8089f940e0d4bd1f04e740d93062", + "file": "src/click/core.py", + "line": 2237, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Parameter(ABC):\n r\"\"\"A parameter to a command comes in two versions: they are either\n :class:`Option`\\s or :class:`Argument`\\s. Other subclasses are currently\n not supported by design as some of the internals for parsing are" + }, + { + "repository": "click", + "tool": "compass", + "rank": 3, + "label": "Command", + "degree": 85, + "role": "type", + "id": "sha256:e822a51cf5afc73cfb7f3fccc077fd04bcb61da80b77998f22fb4b663ac4ea66", + "file": "src/click/core.py", + "line": 985, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Command:\n \"\"\"Commands are the basic building block of command line interfaces in\n Click. A basic command handles command line parsing and might dispatch\n more parsing to commands nested below it." + }, + { + "repository": "click", + "tool": "compass", + "rank": 4, + "label": "Option", + "degree": 51, + "role": "type", + "id": "sha256:6157d30f9a6fd24baf7d621b601792eb67eb2d7b9d3e185f7a44d30dee887d5b", + "file": "src/click/core.py", + "line": 2963, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Option(Parameter):\n \"\"\"Options are usually optional values on the command line and\n have some extra features that arguments don't have.\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 5, + "label": "Group", + "degree": 49, + "role": "type", + "id": "sha256:15b0848405f27a8502a9efc8d6139bf542482cedc1eacb6becaddfe554f3df0f", + "file": "src/click/core.py", + "line": 1699, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Group(Command):\n \"\"\"A group is a command that nests other commands (or more groups).\n\n :param name: The name of the group command." + }, + { + "repository": "click", + "tool": "compass", + "rank": 6, + "label": "ParamType", + "degree": 40, + "role": "type", + "id": "sha256:1f00115a5f86bc3b6514dbac1daa253d8cfaafa43b58b4f6cea5ccac733d678b", + "file": "src/click/types.py", + "line": 65, + "sourceFileSha256": "30a39185da37bbd98ef47153f7bfd310b04eac44bd9b9b2fe15998406337757e", + "sourceText": "class ParamType(t.Generic[_ValueT_co, _InputT_contra], abc.ABC):\n \"\"\"Represents the type of a parameter. Validates and converts values\n from the command line or Python into the correct type.\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 7, + "label": "HelpFormatter", + "degree": 37, + "role": "type", + "id": "sha256:a2a7bb3dd442be664b82216a6b3acd8a1e8079c2abb02f67fc7455da68e72648", + "file": "src/click/formatting.py", + "line": 110, + "sourceFileSha256": "f125b628692f8dfcfd43535b7a88cc1ee64137471f9d0243b389aa0cfea85e6b", + "sourceText": "class HelpFormatter:\n \"\"\"This class helps with formatting text-based help pages. It's\n usually just needed for very special internal cases, but it's also\n exposed so that developers can write their own fancy outputs." + }, + { + "repository": "click", + "tool": "compass", + "rank": 8, + "label": "echo()", + "degree": 36, + "role": "callable", + "id": "sha256:abda0c42f0c49427afee855d0256edc7b5b579da8e43dbcf33162ee7992d3c8a", + "file": "src/click/utils.py", + "line": 240, + "sourceFileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17", + "sourceText": "def echo(\n message: object = None,\n file: t.IO[t.Any] | None = None,\n nl: bool = True," + }, + { + "repository": "click", + "tool": "compass", + "rank": 9, + "label": "_get_words()", + "degree": 26, + "role": "test-helper", + "id": "sha256:dee697366b1c46d71686a3eccb36489b42833ef72a15c620ad3f874059c3e733", + "file": "tests/test_shell_completion.py", + "line": 29, + "sourceFileSha256": "d45f0b1b42850463a3a07273989518d477f8b19c4ca0d8ef74dc29c309276bd8", + "sourceText": "def _get_words(cli, args, incomplete):\n return [c.value for c in _get_completions(cli, args, incomplete)]\n\n" + }, + { + "repository": "click", + "tool": "compass", + "rank": 10, + "label": "Argument", + "degree": 25, + "role": "type", + "id": "sha256:23ddee6a475e892ac17dd6dee048b0da67616a649067b5918fab30b499796578", + "file": "src/click/core.py", + "line": 3800, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Argument(Parameter):\n \"\"\"Arguments are positional parameters to a command. They generally\n provide fewer features than options but can have infinite ``nargs``\n and are required by default." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 1, + "label": "Context", + "degree": 174, + "role": "type", + "id": "src_click_core_context", + "file": "src/click/core.py", + "line": 234, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Context:\n \"\"\"The context is a special internal object that holds state relevant\n for the script execution at every single level. It's normally invisible\n to commands unless they opt-in to getting access to it." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 2, + "label": "Command", + "degree": 100, + "role": "type", + "id": "src_click_core_command", + "file": "src/click/core.py", + "line": 985, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Command:\n \"\"\"Commands are the basic building block of command line interfaces in\n Click. A basic command handles command line parsing and might dispatch\n more parsing to commands nested below it." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 3, + "label": "Parameter", + "degree": 91, + "role": "type", + "id": "src_click_core_parameter", + "file": "src/click/core.py", + "line": 2237, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Parameter(ABC):\n r\"\"\"A parameter to a command comes in two versions: they are either\n :class:`Option`\\s or :class:`Argument`\\s. Other subclasses are currently\n not supported by design as some of the internals for parsing are" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 4, + "label": "CliRunner", + "degree": 65, + "role": "type", + "id": "src_click_testing_clirunner", + "file": "src/click/testing.py", + "line": 317, + "sourceFileSha256": "76f4e649bc53000d667c8c0deff1591970fa034b0f085a207165839b2301019a", + "sourceText": "class CliRunner:\n \"\"\"The CLI runner provides functionality to invoke a Click command line\n script for unittesting purposes in a isolated environment. This only\n works in single-threaded systems without any concurrency as it changes the" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 5, + "label": "Option", + "degree": 55, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 6, + "label": "cmd()", + "degree": 45, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 7, + "label": "Group", + "degree": 44, + "role": "type", + "id": "src_click_core_group", + "file": "src/click/core.py", + "line": 1699, + "sourceFileSha256": "53df54afb5deba7fd7c3a968a2a6284fc2d9dd3892e6939dfbb3092ca3fbca0f", + "sourceText": "class Group(Command):\n \"\"\"A group is a command that nests other commands (or more groups).\n\n :param name: The name of the group command." + }, + { + "repository": "click", + "tool": "graphify", + "rank": 8, + "label": "option()", + "degree": 37, + "role": "callable", + "id": "src_click_decorators_option", + "file": "src/click/decorators.py", + "line": 352, + "sourceFileSha256": "16069357615691fcdfc9c794bb515f6009eb35aad6f7a76c017bdce06dae8d55", + "sourceText": "def option(\n *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any\n) -> t.Callable[[FC], FC]:\n \"\"\"Attaches an option to the command. All positional arguments are" + }, + { + "repository": "click", + "tool": "graphify", + "rank": 9, + "label": "echo()", + "degree": 37, + "role": null + }, + { + "repository": "click", + "tool": "graphify", + "rank": 10, + "label": "Choice", + "degree": 31, + "role": "type", + "id": "src_click_types_choice", + "file": "src/click/types.py", + "line": 342, + "sourceFileSha256": "30a39185da37bbd98ef47153f7bfd310b04eac44bd9b9b2fe15998406337757e", + "sourceText": "class Choice(ParamType[_ValueT_co], t.Generic[_ValueT_co]):\n \"\"\"The choice type allows a value to be checked against a fixed set\n of supported values.\n" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 1, + "label": ".parse()", + "degree": 798, + "role": "callable", + "id": "sha256:f7d4781fbbe1422803605dbf458fa8a1db80c4d645eb71314550f10d633b9866", + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 77, + "sourceFileSha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7", + "sourceText": " public static Document parse(String html) {\n return Parser.parse(html, \"\");\n }\n" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 2, + "label": "Element", + "degree": 635, + "role": "type", + "id": "sha256:95c73544da32be6d2e83b3eebff33a5c4e0119744fa6da3e4957a661dfa20956", + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 50, + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "sourceText": "public class Element extends Node implements Iterable {\n private static final List EmptyChildren = Collections.emptyList();\n private static final NodeList EmptyNodeList = new NodeList(0);\n static final String BaseUriKey = Attributes.internalKey(\"baseUri\");" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 3, + "label": "Node", + "degree": 295, + "role": "type", + "id": "sha256:cb5d1a2ae3e71d4c6956fc589767e1bfc7f1b6a9d47417ca5acf37308c46bd2c", + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 26, + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "sourceText": "public abstract class Node implements Cloneable {\n static final List EmptyNodes = Collections.emptyList();\n static final String EmptyString = \"\";\n @Nullable Element parentNode; // Nodes don't always have parents" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 4, + "label": "HtmlParserTest", + "degree": 284, + "role": "test-type", + "id": "sha256:de2de457e5558493b588e65ee2aa2d3cb6ffbce7cefe413a70ad228e62f72f85", + "file": "src/test/java/org/jsoup/parser/HtmlParserTest.java", + "line": 32, + "sourceFileSha256": "859f7ccb7c09558e54328e0eb6001f8fcc5b84e67c71b0bd2533ffe070495719", + "sourceText": "public class HtmlParserTest {\n\n @Test public void parsesSimpleDocument() {\n String html = \"First!

First post!

\";" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 5, + "label": "ElementTest", + "degree": 261, + "role": "test-type", + "id": "sha256:41d1bdb7999a0b8d28dde83783ce8473f14b015b88cdebaf451f2d965f60b9c9", + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 46, + "sourceFileSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc", + "sourceText": "public class ElementTest {\n private final String reference = \"

Hello

Another element

\";\n\n private static void validateScriptContents(String src, Element el) {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 6, + "label": "CharacterReader", + "degree": 206, + "role": "type", + "id": "sha256:8dbd9566f2d7185705a924a0e685666f5b58349f89d04c2d7f7c1f2ade5250f1", + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 22, + "sourceFileSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c", + "sourceText": "public final class CharacterReader implements AutoCloseable {\n static final char EOF = (char) -1;\n private static final int MaxStringCacheLen = 12;\n private static final int StringCacheSize = 512;" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 7, + "label": "HtmlTreeBuilder", + "degree": 203, + "role": "type", + "id": "sha256:448d5953d9c1e7f7d0129715551893b4a930bca8a120832d6895ab762e0f9337", + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 33, + "sourceFileSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f", + "sourceText": "public class HtmlTreeBuilder extends TreeBuilder {\n static final String[] TagMathMlTextIntegration = new String[]{\"mi\", \"mn\", \"mo\", \"ms\", \"mtext\"};\n static final String[] TagSvgHtmlIntegration = new String[]{\"desc\", \"foreignObject\", \"title\"};\n static final String[] TagFormListed = {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 8, + "label": ".body()", + "degree": 187, + "role": "callable", + "id": "sha256:f4f51f52089ad7a2a597230a89bbae7dd488d3e3a2f4d872df44b6325e6aa1d8", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 155, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": " public Element body() {\n final Element html = htmlEl();\n Element el = html.firstElementChild();\n while (el != null) {" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 9, + "label": "Document", + "degree": 182, + "role": "type", + "id": "sha256:f947ec4bb8822b5d9607c6786bb89ebe182f9c5d31ccf7b829fa583f5fa9bd82", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 26, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": "public class Document extends Element {\n private @Nullable Connection connection; // the connection this doc was fetched from, if any\n private OutputSettings outputSettings = new OutputSettings();\n private Parser parser; // the parser used to parse this document" + }, + { + "repository": "jsoup", + "tool": "compass", + "rank": 10, + "label": ".stripNewlines()", + "degree": 177, + "role": "test-helper", + "id": "sha256:5517e1de0e519d02805292870167f5e1176438f58f9336b90139ee55cf9c59f4", + "file": "src/test/java/org/jsoup/TextUtil.java", + "line": 16, + "sourceFileSha256": "48216be48aa35dae2459fc3f2ea487abda9e3a6924dae3c0b661a4fd6cc684d2", + "sourceText": " public static String stripNewlines(String text) {\n return stripper.matcher(text).replaceAll(\"\");\n }\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 1, + "label": "Element", + "degree": 415, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_element_element", + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 50, + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "sourceText": "public class Element extends Node implements Iterable {\n private static final List EmptyChildren = Collections.emptyList();\n private static final NodeList EmptyNodeList = new NodeList(0);\n static final String BaseUriKey = Attributes.internalKey(\"baseUri\");" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 2, + "label": "HtmlParserTest", + "degree": 284, + "role": "test-type", + "id": "src_test_java_org_jsoup_parser_htmlparsertest_htmlparsertest", + "file": "src/test/java/org/jsoup/parser/HtmlParserTest.java", + "line": 32, + "sourceFileSha256": "859f7ccb7c09558e54328e0eb6001f8fcc5b84e67c71b0bd2533ffe070495719", + "sourceText": "public class HtmlParserTest {\n\n @Test public void parsesSimpleDocument() {\n String html = \"First!

First post!

\";" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 3, + "label": "ElementTest", + "degree": 260, + "role": "test-type", + "id": "src_test_java_org_jsoup_nodes_elementtest_elementtest", + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 46, + "sourceFileSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc", + "sourceText": "public class ElementTest {\n private final String reference = \"

Hello

Another element

\";\n\n private static void validateScriptContents(String src, Element el) {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 4, + "label": "Node", + "degree": 234, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_node_node", + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 26, + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "sourceText": "public abstract class Node implements Cloneable {\n static final List EmptyNodes = Collections.emptyList();\n static final String EmptyString = \"\";\n @Nullable Element parentNode; // Nodes don't always have parents" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 5, + "label": "HtmlTreeBuilder", + "degree": 162, + "role": "type", + "id": "src_main_java_org_jsoup_parser_htmltreebuilder_htmltreebuilder", + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 33, + "sourceFileSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f", + "sourceText": "public class HtmlTreeBuilder extends TreeBuilder {\n static final String[] TagMathMlTextIntegration = new String[]{\"mi\", \"mn\", \"mo\", \"ms\", \"mtext\"};\n static final String[] TagSvgHtmlIntegration = new String[]{\"desc\", \"foreignObject\", \"title\"};\n static final String[] TagFormListed = {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 6, + "label": "SelectorTest", + "degree": 160, + "role": "test-type", + "id": "src_test_java_org_jsoup_select_selectortest_selectortest", + "file": "src/test/java/org/jsoup/select/SelectorTest.java", + "line": 31, + "sourceFileSha256": "0560f38b89cb82e8f7e12386d468b43bd069874941be5fa85ad3227363e18d66", + "sourceText": "public class SelectorTest {\n\n /** Test that the selected elements match exactly the specified IDs. */\n public static void assertSelectedIds(Elements els, String... ids) {" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 7, + "label": "CharacterReader", + "degree": 139, + "role": "type", + "id": "src_main_java_org_jsoup_parser_characterreader_characterreader", + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 22, + "sourceFileSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c", + "sourceText": "public final class CharacterReader implements AutoCloseable {\n static final char EOF = (char) -1;\n private static final int MaxStringCacheLen = 12;\n private static final int StringCacheSize = 512;" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 8, + "label": "Evaluator", + "degree": 134, + "role": "type", + "id": "src_main_java_org_jsoup_select_evaluator_evaluator", + "file": "src/main/java/org/jsoup/select/Evaluator.java", + "line": 31, + "sourceFileSha256": "5a3ce0742ddf5d5bb647e8627735c47c656f5642361f41fbb30f26fa7b6f7e8d", + "sourceText": "public abstract class Evaluator {\n protected Evaluator() {\n }\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 9, + "label": "Tokeniser", + "degree": 122, + "role": "type", + "id": "src_main_java_org_jsoup_parser_tokeniser_tokeniser", + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "line": 16, + "sourceFileSha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f", + "sourceText": "final class Tokeniser {\n static final char replacementChar = '\\uFFFD'; // replaces null character\n private static final char[] notCharRefCharsSorted = new char[]{'\\t', '\\n', '\\r', '\\f', ' ', '<', '&'};\n" + }, + { + "repository": "jsoup", + "tool": "graphify", + "rank": 10, + "label": "Document", + "degree": 121, + "role": "type", + "id": "src_main_java_org_jsoup_nodes_document_document", + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 26, + "sourceFileSha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502", + "sourceText": "public class Document extends Element {\n private @Nullable Connection connection; // the connection this doc was fetched from, if any\n private OutputSettings outputSettings = new OutputSettings();\n private Parser parser; // the parser used to parse this document" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 1, + "label": "createStore.spec", + "degree": 159, + "role": "test-module", + "id": "sha256:74fe6fa433c85236a87bda7c1d2beb4256849ee0e3e4c16dac078f89c8d7251d", + "file": "test/createStore.spec.ts", + "line": 1, + "sourceFileSha256": "28b82562daeb4dbbce155397db1825c1b47e28cdac317033bd1655619b5eadb6", + "sourceText": "import type { StoreEnhancer, Action, Store, Reducer } from 'redux'\nimport { createStore, combineReducers } from 'redux'\nimport { vi } from 'vitest'\nimport {" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 2, + "label": "combineReducers.spec", + "degree": 84, + "role": "test-module", + "id": "sha256:10a60eda4e3187f3fc3119318562b3e286b7a8903bd64be8946ec37494becf44", + "file": "test/combineReducers.spec.ts", + "line": 1, + "sourceFileSha256": "4dc22297b4938fa7278dde5350870e2fa31c8ff87a7d48b943a3b8fc64ddfb84", + "sourceText": "/* oxlint-disable no-console */\nimport type { Reducer, Action } from 'redux'\nimport {\n createStore," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 3, + "label": "defaultAssembleReplacementNodes()", + "degree": 74, + "role": "documentation-tooling", + "id": "sha256:9695b4120077fb0d683e8f491bae870400b249ec2652a51e73203216ba7b5f60", + "file": "website/plugins/remark-typescript-tools/transpileCodeblocks/plugin.ts", + "line": 215, + "sourceFileSha256": "dd7cbd61846175fcad25c515ffa2c4c95d44ec7b739dd3dcc11e181c59d3c3f7", + "sourceText": "export function defaultAssembleReplacementNodes(\n node: CodeNode,\n file: VFile,\n virtualFolder: string," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 4, + "label": "index", + "degree": 70, + "role": "source-module", + "id": "sha256:dce3d43d2e05a1e2cff376a47a40c65b4c8879f6e24b0f34fb2bc0dd19d219fa", + "file": "src/index.ts", + "line": 1, + "sourceFileSha256": "8bb0a6852611119382cae7dc89ab600d43701c87644e518042ab23134a4c16a9", + "sourceText": "// functions\nimport { createStore, legacy_createStore } from './createStore'\nimport combineReducers from './combineReducers'\nimport bindActionCreators from './bindActionCreators'" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 5, + "label": "reducers.test-d", + "degree": 68, + "role": "test-module", + "id": "sha256:f1fdcad28d45e9456b801123f7cb03ae12f0875f58aa73a52e3abd8a73e0b3f7", + "file": "test/typescript/reducers.test-d.ts", + "line": 1, + "sourceFileSha256": "6e9d92a666fef0546948aa96d7e7d14b96f31c3f660074039d8cc0bb8a1e5d0e", + "sourceText": "import type {\n Action,\n AnyAction,\n PreloadedStateShapeFromReducersMapObject," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 6, + "label": "transpileCodeblocks()", + "degree": 56, + "role": "documentation-tooling", + "id": "sha256:e1a2859c6983979bae899fd5f06a8e0d221aa1811cb1d42787ad0ac98e8067ea", + "file": "website/plugins/remark-typescript-tools/transpileCodeblocks/plugin.ts", + "line": 46, + "sourceFileSha256": "dd7cbd61846175fcad25c515ffa2c4c95d44ec7b739dd3dcc11e181c59d3c3f7", + "sourceText": "export const transpileCodeblocks: Plugin<[TranspileCodeblocksSettings]> =\n function ({\n compilerSettings,\n postProcessTranspiledJs = defaultPostProcessTranspiledJs," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 7, + "label": "enhancers.test-d", + "degree": 47, + "role": "test-module", + "id": "sha256:70cc954750611610bf9773a8c59e07344ac1f8fafc7107565c5c9e346dccbf5b", + "file": "test/typescript/enhancers.test-d.ts", + "line": 1, + "sourceFileSha256": "12fea8393645a50c8be7ee2b1b81839bcb903e80c7394361ba60268e80ad4313", + "sourceText": "import type { Action, Reducer, StoreEnhancer } from 'redux'\nimport { createStore } from 'redux'\n\ninterface State {" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 8, + "label": "store.test-d", + "degree": 35, + "role": "test-module", + "id": "sha256:9bf18a6ffd0f35c3ce4b2e33d8ef1bcd285fc930e09955b98176f15289db3124", + "file": "test/typescript/store.test-d.ts", + "line": 1, + "sourceFileSha256": "ad0d2a2e2490699420a14afc689bd57d7967d5220e41f97c257b86c90098bd77", + "sourceText": "import type {\n Action,\n Observer,\n Reducer," + }, + { + "repository": "redux", + "tool": "compass", + "rank": 9, + "label": "store", + "degree": 33, + "role": "source-module", + "id": "sha256:802d7602df7b8cac096452350c66b4dd52d05861d0dee4b3ba6f86421bdf2db4", + "file": "src/types/store.ts", + "line": 1, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "import type { Action, UnknownAction } from './actions'\nimport type { Reducer } from './reducers'\n// oxlint-disable-next-line typescript/no-unused-vars\nimport _$$observable from '../utils/symbol-observable'" + }, + { + "repository": "redux", + "tool": "compass", + "rank": 10, + "label": "createStore()", + "degree": 33, + "role": "callable", + "id": "sha256:ceae4b39dd8902b6888096a1827ee67422d04d3c19d527d674ec740c3e903785", + "file": "src/createStore.ts", + "line": 86, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceText": "export function createStore<\n S,\n A extends Action,\n Ext extends {} = {}," + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 1, + "label": "Action", + "degree": 23, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 2, + "label": "createStore()", + "degree": 22, + "role": "callable", + "id": "src_createstore_createstore", + "file": "src/createStore.ts", + "line": 86, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceText": "export function createStore<\n S,\n A extends Action,\n Ext extends {} = {}," + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 3, + "label": "compilerOptions", + "degree": 21, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 4, + "label": "compilerOptions", + "degree": 19, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 5, + "label": "compilerOptions", + "degree": 16, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 6, + "label": "scripts", + "degree": 14, + "role": null + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 7, + "label": "Dispatch", + "degree": 13, + "role": "type", + "id": "src_types_store_dispatch", + "file": "src/types/store.ts", + "line": 27, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "export interface Dispatch
{\n (action: T, ...extraArgs: any[]): T\n}\n" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 8, + "label": "Reducer", + "degree": 12, + "role": "type", + "id": "src_types_reducers_reducer", + "file": "src/types/reducers.ts", + "line": 30, + "sourceFileSha256": "f79a73490d143561e6071c03d852fc78fe057384a6a3e6ff81b81e887df76bc1", + "sourceText": "export type Reducer<\n S = any,\n A extends Action = UnknownAction,\n PreloadedState = S" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 9, + "label": "combineReducers()", + "degree": 12, + "role": "callable", + "id": "src_combinereducers_combinereducers", + "file": "src/combineReducers.ts", + "line": 123, + "sourceFileSha256": "52f4a8c5561bd9ea2532edd31d3a8958dabb13fcc0d11140d158c84e454ce1dc", + "sourceText": "export default function combineReducers(reducers: {\n [key: string]: Reducer\n}) {\n const reducerKeys = Object.keys(reducers)" + }, + { + "repository": "redux", + "tool": "graphify", + "rank": 10, + "label": "Store", + "degree": 11, + "role": "type", + "id": "src_types_store_store", + "file": "src/types/store.ts", + "line": 81, + "sourceFileSha256": "14d07fb4a3bd59122730edd9c5080fa051f3ae13df1095a767358ec69d67886b", + "sourceText": "export interface Store<\n S = any,\n A extends Action = UnknownAction,\n StateExt extends unknown = unknown" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 1, + "label": "DirEntry", + "degree": 67, + "role": "type", + "id": "sha256:9102d21ed875d3f823b6a2b6c19b5c9ec4ec6d5734b63c544d1ede2b4fb09374", + "file": "src/dent.rs", + "line": 35, + "sourceFileSha256": "ca573f4533370a09851579f5940f7cd9bd121b2f30ec51d29a40afdce984683b", + "sourceText": "pub struct DirEntry {\n /// The path as reported by the [`fs::ReadDir`] iterator (even if it's a\n /// symbolic link).\n ///" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 2, + "label": ".new()", + "degree": 51, + "role": "callable", + "id": "sha256:38839ab8bc007bfdd70a2ed3abfb3a77950fb3d008d621d9cf7c5c9bbc8f8d6a", + "file": "src/lib.rs", + "line": 289, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": " pub fn new>(root: P) -> Self {\n WalkDir {\n opts: WalkDirOptions {\n follow_links: false," + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 3, + "label": ".tmp()", + "degree": 50, + "role": "test-helper", + "id": "sha256:0dbcc8c36333b467c48ece7070677fe49f658cd8454b9fd76a40ed7303319fa4", + "file": "src/tests/util.rs", + "line": 84, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn tmp() -> Dir {\n let dir = TempDir::new().unwrap();\n Dir { dir }\n }" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 4, + "label": ".run_recursive()", + "degree": 50, + "role": "test-helper", + "id": "sha256:caafb698ff937854c3b5f77147d26689bf26777be7dca6bcee83d425593d53f8", + "file": "src/tests/util.rs", + "line": 101, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn run_recursive(&self, it: I) -> RecursiveResults\n where\n I: IntoIterator>,\n {" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 5, + "label": ".path()", + "degree": 42, + "role": "test-helper", + "id": "sha256:76ab83daadb4f80254042a7ee65d7cdda5f0c66fa708172245f102572ca08106", + "file": "src/tests/util.rs", + "line": 90, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn path(&self) -> &Path {\n self.dir.path()\n }\n" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 6, + "label": ".mkdirp()", + "degree": 34, + "role": "test-helper", + "id": "sha256:6984d05046d66fab178a4dcaf88c3814c5030b5bfa795fd5d68a3c14469cb71b", + "file": "src/tests/util.rs", + "line": 117, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn mkdirp>(&self, path: P) {\n let full = self.join(path);\n fs::create_dir_all(&full)\n .map_err(|e| {" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 7, + "label": "Error", + "degree": 31, + "role": "type", + "id": "sha256:a66dc1de7a258eafe92e616e5c94a0107fda891db6f9ca33ca1e10fbac5845c9", + "file": "src/error.rs", + "line": 28, + "sourceFileSha256": "ba58bf6f59d196567435d4b66699a928cc237fc7c8df01dc37ab822509905b7c", + "sourceText": "pub struct Error {\n depth: usize,\n inner: ErrorInner,\n}" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 8, + "label": "WalkDir", + "degree": 31, + "role": "type", + "id": "sha256:b647b5809af96e790d083521ec5b9ee189e2a2446b6a08294da8300d43fc420e", + "file": "src/lib.rs", + "line": 234, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct WalkDir {\n opts: WalkDirOptions,\n root: PathBuf,\n}" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 9, + "label": "Result", + "degree": 27, + "role": "type", + "id": "sha256:b0a84ceb03a1b40af4acbce9ba086ead4d7cfc8679eb1cda58f7024a9899c6be", + "file": "src/lib.rs", + "line": 157, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub type Result = ::std::result::Result;\n\n/// A builder to create an iterator for recursively walking a directory.\n///" + }, + { + "repository": "walkdir", + "tool": "compass", + "rank": 10, + "label": ".touch()", + "degree": 22, + "role": "test-helper", + "id": "sha256:062bcfac2f5cea7a99a078422476e89eb7977023b3d5c265c534e7a2b0ca6c99", + "file": "src/tests/util.rs", + "line": 128, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": " pub fn touch>(&self, path: P) {\n let full = self.join(path);\n File::create(&full)\n .map_err(|e| {" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 1, + "label": "DirEntry", + "degree": 39, + "role": "type", + "id": "src_dent_direntry", + "file": "src/dent.rs", + "line": 35, + "sourceFileSha256": "ca573f4533370a09851579f5940f7cd9bd121b2f30ec51d29a40afdce984683b", + "sourceText": "pub struct DirEntry {\n /// The path as reported by the [`fs::ReadDir`] iterator (even if it's a\n /// symbolic link).\n ///" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 2, + "label": "IntoIter", + "degree": 22, + "role": "type", + "id": "src_lib_intoiter", + "file": "src/lib.rs", + "line": 566, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct IntoIter {\n /// Options specified in the builder. Depths, max fds, etc.\n opts: WalkDirOptions,\n /// The start path." + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 3, + "label": "WalkDir", + "degree": 19, + "role": "type", + "id": "src_lib_walkdir", + "file": "src/lib.rs", + "line": 234, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "pub struct WalkDir {\n opts: WalkDirOptions,\n root: PathBuf,\n}" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 4, + "label": "WalkDirOptions", + "degree": 12, + "role": "type", + "id": "src_lib_walkdiroptions", + "file": "src/lib.rs", + "line": 239, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "struct WalkDirOptions {\n follow_links: bool,\n follow_root_links: bool,\n max_open: usize," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 5, + "label": "Dir", + "degree": 11, + "role": "test-type", + "id": "src_tests_util_dir", + "file": "src/tests/util.rs", + "line": 78, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": "pub struct Dir {\n dir: TempDir,\n}\n" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 6, + "label": "RecursiveResults", + "degree": 11, + "role": "test-type", + "id": "src_tests_util_recursiveresults", + "file": "src/tests/util.rs", + "line": 23, + "sourceFileSha256": "ca72ef96f82bb87d8c93d13d581ebb65efcd53fffa87097a84437acecbc30faa", + "sourceText": "pub struct RecursiveResults {\n ents: Vec,\n errs: Vec,\n}" + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 7, + "label": "DirList", + "degree": 10, + "role": "type", + "id": "src_lib_dirlist", + "file": "src/lib.rs", + "line": 661, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceText": "enum DirList {\n /// An opened handle.\n ///\n /// This includes the depth of the handle itself." + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 8, + "label": "Args", + "degree": 10, + "role": "example-type", + "id": "walkdir_list_main_args", + "file": "walkdir-list/main.rs", + "line": 156, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "struct Args {\n dirs: Vec,\n follow_links: bool,\n min_depth: Option," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 9, + "label": "print_paths_flat()", + "degree": 9, + "role": "example-callable", + "id": "walkdir_list_main_print_paths_flat", + "file": "walkdir-list/main.rs", + "line": 102, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "fn print_paths_flat(\n args: &Args,\n mut stdout: W1,\n mut stderr: W2," + }, + { + "repository": "walkdir", + "tool": "graphify", + "rank": 10, + "label": "print_paths_tree()", + "degree": 9, + "role": "example-callable", + "id": "walkdir_list_main_print_paths_tree", + "file": "walkdir-list/main.rs", + "line": 128, + "sourceFileSha256": "d87ed239df1f120d2dc8711e83f02d26785396b7bc15d6c865490cd9ab2d7886", + "sourceText": "fn print_paths_tree(\n args: &Args,\n mut stdout: W1,\n mut stderr: W2," + } + ] +} diff --git a/benchmarks/agent_query/mcp_panel_a_review.json b/benchmarks/agent_query/mcp_panel_a_review.json new file mode 100644 index 000000000..8856aa84f --- /dev/null +++ b/benchmarks/agent_query/mcp_panel_a_review.json @@ -0,0 +1,485 @@ +{ + "afterRouteCorrection": { + "binarySha256": "872aff05f6c1723d27ee96230a4e315d3f0d6e1849387de09530b907f0ff3f40", + "capture": "mcp-panel-a-02", + "cli": { + "questionsPerTool": 55, + "sourcePathMatches": { + "compass": 6, + "graphify": 8 + }, + "successfulExecutions": 98, + "textPasses": { + "compass": 46, + "graphify": 46 + }, + "timeouts": 0 + }, + "edgeAudit": { + "corrected": { + "compass": { + "occurrenceAgreement": 16, + "pairs": 21, + "relationshipMatches": 16 + }, + "graphify": { + "occurrenceAgreement": 17, + "pairs": 21, + "relationshipMatches": 20 + } + }, + "registered": { + "compass": { + "occurrenceAgreement": 15, + "pairs": 21, + "relationshipMatches": 16 + }, + "graphify": { + "occurrenceAgreement": 18, + "pairs": 21, + "relationshipMatches": 20 + } + } + }, + "expectedQuestionMultisetVerified": true, + "relationshipDelta": { + "chi": { + "added": 0, + "baseline": "go-initializer-chi-02", + "nodeCountUnchanged": true, + "removedContains": 88 + }, + "otherFourRepositories": "No relationship changes against frozen panel A." + }, + "scope": "Repeated development MCP questions on fresh panel graphs, including both the prior Go receiver correction and the route-hierarchy correction. The two changes are separated in the relationship delta.", + "successfulRequests": 60, + "tools": { + "compass": { + "ambiguityPreserved": 5, + "communityMembers": { + "chi": 90, + "click": 389, + "jsoup": 752, + "redux": 271, + "walkdir": 116 + }, + "hubIdentitiesKnown": 50, + "hubSummaryMatches": 50, + "membershipMatches": 5, + "missingCommunityMatches": 5, + "neighborDisplayedTriplesMatch": 5, + "sourceRoles": { + "callable": 7, + "documentation-tooling": 2, + "source-module": 2, + "test-helper": 9, + "test-module": 5, + "test-type": 2, + "type": 23 + }, + "statsMatches": 5, + "textBytes": 125192, + "wireResponseBytes": 174698 + }, + "graphify": { + "ambiguityPreserved": 4, + "communityMembers": { + "chi": 108, + "click": 84, + "jsoup": 295, + "redux": 71, + "walkdir": 47 + }, + "hubIdentitiesKnown": 41, + "hubSummaryMatches": 0, + "membershipMatches": 5, + "missingCommunityMatches": 5, + "neighborDisplayedTriplesMatch": 5, + "sourceRoles": { + "callable": 5, + "example-callable": 2, + "example-type": 1, + "test-helper": 2, + "test-type": 5, + "type": 26, + "unverified-identity": 9 + }, + "statsMatches": 5, + "textBytes": 42550, + "wireResponseBytes": 45989 + } + }, + "verification": { + "benchmarkTestsPassed": 97, + "clippy": "passed on workspace lib/bin plus selected integrations; first run retained an existing expect_err lint failure, corrected with an explicit error assertion", + "frameworkQualificationTestsRerunAfterLintCleanup": 4, + "frameworkRoutesPassed": 17, + "fullFixtureQualification": "pending; previous Go fixture pass does not verify this new production change", + "nativeFailed": 0, + "nativeIgnored": 2, + "nativePassed": 1431 + } + }, + "artifactDigests": { + "chi-route-hierarchy-negative-01.json": "487ad2d0338b165d2f835c2bc0bea34a299132ccc14602c9540e6907e332d5f6", + "chi-route-hierarchy-negative-02.json": "4c2a59863ccc1abbbcd28ee0ea52b810dbbc8b0775258aa7cdeb27e26a588e84", + "chi-route-hierarchy-negative-03.json": "aa2ba8b6d0a170ce1d9b61916dcc9471bfcfa863c9db107064657b871cb8dc5f", + "mcp-panel-a-01/run.json": "af1f84bee444ea3ab05e2db839562e7e4b392f5f1b1da8cba7e91fca5aa5dd28", + "mcp-panel-a-02/run.json": "c001978913423a45102f74cd8f079cf78a12e1b425f40c9451ca8725b416ea91", + "mcp-panel-a-hub-source-audit-01.capture.json": "4429a6b4d024fcbf079143a09b31c0d5fc3992c2a1799b8d0f52184eebe85f46", + "mcp-panel-a-hub-source-audit-01.json": "9716c6036f6d6945573104c6ee5c1f2c744e11f7149a95e681d911c26f78365a", + "mcp-panel-a-hub-source-audit-02.capture.json": "1fcb887ec31d870ac5965d65e8dfa8dccc752722d898e65e1be7ac179a2e71cb", + "mcp-panel-a-hub-source-audit-02.json": "9336850fce9160bb279f90aa0b18b278282211108706293ef4d3c3758f16c771", + "mcp-panel-a-python-01.log": "5929e889d4946028b77b12e360bd7d58ad6647a59395dfbf0af795e848cc454f", + "route-hierarchy-clippy-01.log": "82866b969b6b661a2ab3ba7ef1ac68092a6d0877da9c7d8decb4f7a114fc9941", + "route-hierarchy-clippy-02.log": "51ff0ed8555a37a44dec965e9314732c8b97ebb4fec4a68455409edfa84b1a57", + "route-hierarchy-native-01.log": "3019788711eb5bc49a2f7e0eec25d17c68efdf73b4cba7644dad18be1fadc026", + "route-hierarchy-panel-a-delta-01.json": "475947f81502460731f2af67e93a4857064413fd88543229a8edbae763f0dbe4", + "route-hierarchy-panel-a-path-audit-01.json": "528a6546ff74891b19b8bdcff6867816d9d4d6901af9cab2a15587d562d61d6d", + "route-hierarchy-qualification-test-02.log": "161fd8e342e86b2da8325e6c4fc8163e364ac5fcf8ebe047d1d915227c3094bc", + "route-hierarchy-regression-after-01.log": "1b6eac01a41a13492e502428e4ba9870a794ab3e3993e720c64b2c8706fc7137", + "route-hierarchy-regression-before-01.log": "52dd5af345f849ee575ba1de94a5c7bceefa065c7a9a39a94387c23f45473bc2" + }, + "expectedQuestionMultisetVerified": true, + "findings": [ + "Both tools enumerate their selected largest stored communities completely. Partition sizes and identities differ; this does not compare cohesion.", + "Compass reports ambiguity for all five names but returns broad substring candidates, including documentation nodes. Graphify silently selects the Chi URLParam method and preserves the other four ambiguities.", + "Compass Chi route hub GET /users/1 has degree 59 from 68 unsupported cross-file containment records. A selected negative fails on both the frozen panel graph and the current Go-corrected graph. Graphify has no matching route identities; absence is not scored as a correct negative.", + "Compass raw neighbor text omits call occurrence sites; Graphify prints retained sites. Pair/direction agreement does not prove source occurrence completeness.", + "Largest community membership, degree, and connectivity summaries are self-graph checks. Source-precision and god-object or cohesion judgments remain open." + ], + "largestCommunities": [ + { + "communityId": 0, + "members": 96, + "repository": "chi", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "compass", + "topFiles": [ + [ + "mux_test.go", + 48 + ], + [ + "chi.go", + 27 + ], + [ + "tree.go", + 5 + ], + [ + "mux.go", + 5 + ], + [ + "tree_test.go", + 5 + ] + ] + }, + { + "communityId": 0, + "members": 108, + "repository": "chi", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "graphify", + "topFiles": [ + [ + "mux_test.go", + 48 + ], + [ + "tree_test.go", + 12 + ], + [ + "middleware/middleware_test.go", + 6 + ], + [ + "middleware/wrap_writer_test.go", + 6 + ], + [ + "", + 4 + ] + ] + }, + { + "communityId": 0, + "members": 389, + "repository": "click", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "compass", + "topFiles": [ + [ + "src/click/core.py", + 288 + ], + [ + "src/click/exceptions.py", + 36 + ], + [ + "src/click/types.py", + 29 + ], + [ + "src/click/parser.py", + 15 + ], + [ + "src/click/globals.py", + 11 + ] + ] + }, + { + "communityId": 0, + "members": 84, + "repository": "click", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "graphify", + "topFiles": [ + [ + "src/click/exceptions.py", + 16 + ], + [ + "", + 16 + ], + [ + "src/click/utils.py", + 15 + ], + [ + "src/click/termui.py", + 13 + ], + [ + "src/click/globals.py", + 7 + ] + ] + }, + { + "communityId": 0, + "members": 752, + "repository": "jsoup", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "compass", + "topFiles": [ + [ + "src/test/java/org/jsoup/parser/HtmlParserTest.java", + 207 + ], + [ + "src/test/java/org/jsoup/nodes/ElementTest.java", + 145 + ], + [ + "src/test/java/org/jsoup/select/SelectorTest.java", + 135 + ], + [ + "src/test/java/org/jsoup/select/ElementsTest.java", + 48 + ], + [ + "src/main/java/org/jsoup/select/Elements.java", + 25 + ] + ] + }, + { + "communityId": 0, + "members": 295, + "repository": "jsoup", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "graphify", + "topFiles": [ + [ + "src/test/java/org/jsoup/select/SelectorTest.java", + 150 + ], + [ + "src/test/java/org/jsoup/select/ElementsTest.java", + 44 + ], + [ + "src/test/java/org/jsoup/nodes/ElementTest.java", + 22 + ], + [ + "src/test/java/org/jsoup/select/XpathTest.java", + 16 + ], + [ + "src/test/java/org/jsoup/nodes/CommentTest.java", + 9 + ] + ] + }, + { + "communityId": 0, + "members": 271, + "repository": "redux", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "compass", + "topFiles": [ + [ + "test/createStore.spec.ts", + 156 + ], + [ + "test/helpers/reducers.ts", + 38 + ], + [ + "test/applyMiddleware.spec.ts", + 32 + ], + [ + "test/helpers/actionCreators.ts", + 23 + ], + [ + "test/helpers/actionTypes.ts", + 15 + ] + ] + }, + { + "communityId": 0, + "members": 71, + "repository": "redux", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "graphify", + "topFiles": [ + [ + "examples/shopping-cart/src/actions/index.js", + 6 + ], + [ + "", + 6 + ], + [ + "examples/todos/src/actions/index.js", + 5 + ], + [ + "examples/todos/src/containers/VisibleTodoList.js", + 4 + ], + [ + "examples/todos/src/containers/FilterLink.js", + 3 + ] + ] + }, + { + "communityId": 0, + "members": 116, + "repository": "walkdir", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "compass", + "topFiles": [ + [ + "src/lib.rs", + 55 + ], + [ + "src/dent.rs", + 31 + ], + [ + "src/error.rs", + 21 + ], + [ + "src/tests/util.rs", + 9 + ] + ] + }, + { + "communityId": 0, + "members": 47, + "repository": "walkdir", + "scope": "Stored-file distribution, not source-level functional cohesion.", + "tool": "graphify", + "topFiles": [ + [ + "src/tests/recursive.rs", + 45 + ], + [ + "", + 2 + ] + ] + } + ], + "provenanceLimits": "Compass and Graphify MCP server executable hashes are captured, with Graphify package files verified against the separate MCP environment manifest before/after execution. This does not retroactively attest CLI extraction dependencies. Timings under concurrent compilation are not performance evidence.", + "registrationCommit": "3ec3732a989e5d2cf51aa55438450faea7f84e76", + "requests": 60, + "schema": "compass.mcp-panel-a-review/1", + "scope": "Development MCP extension on panel A after its CLI outputs were observed. Use original frozen panel-A graphs and the hub-evidence Compass binary; commit these questions before MCP execution. Prepared IDs are not scored as node retrieval. Counts and membership measure stored-graph consistency, not source precision, functional community quality, or god-object diagnosis.", + "successfulRequests": 60, + "tools": { + "compass": { + "ambiguityPreserved": 5, + "hubIdentitiesKnown": 50, + "hubSummariesAvailable": 50, + "hubSummaryMatches": 50, + "hubTextBytes": 24401, + "hubWireResponseBytes": 69504, + "membershipMatches": 5, + "missingCommunityMatches": 5, + "neighborDisplayedTriplesMatch": 5, + "neighborSemanticDirectionsMatch": 5, + "sourceRoles": { + "callable": 7, + "documentation-tooling": 2, + "example-route": 1, + "source-module": 2, + "test-helper": 8, + "test-module": 5, + "test-route": 1, + "test-type": 2, + "type": 22 + }, + "statsMatches": 5, + "textBytes": 125033, + "wireResponseBytes": 174128 + }, + "graphify": { + "ambiguityPreserved": 4, + "hubIdentitiesKnown": 41, + "hubSummariesAvailable": 0, + "hubSummaryMatches": 0, + "hubTextBytes": 1473, + "hubWireResponseBytes": 1973, + "membershipMatches": 5, + "missingCommunityMatches": 5, + "neighborDisplayedTriplesMatch": 5, + "neighborSemanticDirectionsMatch": 5, + "sourceRoles": { + "callable": 5, + "example-callable": 2, + "example-type": 1, + "test-helper": 2, + "test-type": 5, + "type": 26, + "unverified-identity": 9 + }, + "statsMatches": 5, + "textBytes": 42550, + "wireResponseBytes": 45989 + } + } +} diff --git a/crates/compass-core/src/build_state.rs b/crates/compass-core/src/build_state.rs index b8c036eee..837c1c2dd 100644 --- a/crates/compass-core/src/build_state.rs +++ b/crates/compass-core/src/build_state.rs @@ -20,6 +20,7 @@ fn current_build_fingerprint() -> String { compass_model::code_graph::CODE_GRAPH_SCHEMA_V1, compass_graph::V1_PUBLICATION_SEMANTICS_VERSION, compass_languages::EXTRACTION_SEMANTICS_VERSION, + compass_languages::FRAMEWORK_PACK_SEMANTICS_VERSION, compass_files::AST_CACHE_VERSION, ] { digest.update(component.as_bytes()); diff --git a/crates/compass-languages/src/frameworks/mod.rs b/crates/compass-languages/src/frameworks/mod.rs index c9ff947d4..fea62bb44 100644 --- a/crates/compass-languages/src/frameworks/mod.rs +++ b/crates/compass-languages/src/frameworks/mod.rs @@ -80,9 +80,9 @@ type TemplateDetector = /// Cache identity for the framework-pack registry. The value is deliberately /// separate from the language producer version: changing framework activation, -/// descriptor capabilities, or resource limits must invalidate framework facts -/// without pretending that the parser/evidence producer changed. -pub const FRAMEWORK_PACK_SEMANTICS_VERSION: &str = "compass.framework-packs/6"; +/// descriptor capabilities, resolution/publication, or resource limits must +/// invalidate framework facts without changing the parser/evidence producer. +pub const FRAMEWORK_PACK_SEMANTICS_VERSION: &str = "compass.framework-packs/7"; /// The concrete implementation stored behind one framework-pack seam. /// diff --git a/crates/compass-resolve/src/frameworks/routes.rs b/crates/compass-resolve/src/frameworks/routes.rs index 17af81f25..c38dc5706 100644 --- a/crates/compass-resolve/src/frameworks/routes.rs +++ b/crates/compass-resolve/src/frameworks/routes.rs @@ -221,6 +221,9 @@ pub fn publish_resolved_routes( let mut route_sources_by_scope = BTreeMap::<(String, String), Vec<(String, String, RawFrameworkAnchor, bool)>>::new(); for (route_id, route) in &route_ids { + if !has_filesystem_route_convention(route) { + continue; + } route_sources_by_scope .entry((route.framework.clone(), route_hierarchy_scope(route))) .or_default() @@ -259,6 +262,9 @@ pub fn publish_resolved_routes( route_parent_sources_by_scope.insert((framework.clone(), scope.clone()), by_directory); } for (child_id, child) in &route_ids { + if !has_filesystem_route_convention(child) { + continue; + } let scope = route_hierarchy_scope(child); let mut selected = None; if let Some(candidates) = @@ -423,13 +429,47 @@ fn route_parent_source_file_indexed( None } +fn has_filesystem_route_convention(route: &RawRouteFact) -> bool { + // Receiver spellings such as `r` are local bindings, not filesystem tree + // identities. Only facts from the explicit file-route producers may use + // physical source directories to infer a parent. Programmatic mounts and + // groups remain the responsibility of their framework's composition rules. + matches!(route.origin, RawFrameworkOrigin::Convention) + && matches!( + (route.framework.as_str(), route.rule.as_deref()), + ( + "next", + Some( + "next-app-router-convention" + | "next-app-route-convention" + | "next-app-route-unresolved-convention" + | "next-pages-api-convention" + | "next-file-route-convention" + ) + ) | ("remix", Some("remix-route-convention")) + | ("react-router", Some("react-router-file-route-convention")) + | ("tanstack-router", Some("tanstack-file-route-convention")) + | ( + "sveltekit", + Some("sveltekit-file-route-convention" | "sveltekit-endpoint-convention") + ) + | ( + "nuxt", + Some("nuxt-file-route-convention" | "nuxt-server-api-convention") + ) + | ( + "astro", + Some("astro-file-route-convention" | "astro-endpoint-convention") + ) + ) +} + /// Return the lexical route-tree owner for hierarchy matching. /// /// A repository can contain independent examples such as /// `test/a/app/...` and `test/b/app/...`; their `/` routes must never compete -/// with one another. The scope is intentionally derived from the portable -/// source identity rather than filesystem state so cached and projected -/// extractions remain deterministic. +/// with one another. The scope is derived from the portable source identity +/// rather than filesystem state so cached facts remain deterministic. fn route_hierarchy_scope(route: &RawRouteFact) -> String { let source = route .anchor diff --git a/crates/compass-resolve/tests/framework_qualification.rs b/crates/compass-resolve/tests/framework_qualification.rs index 1a528aa87..110b77381 100644 --- a/crates/compass-resolve/tests/framework_qualification.rs +++ b/crates/compass-resolve/tests/framework_qualification.rs @@ -75,15 +75,14 @@ fn qualification_rejects_unresolved_or_missing_framework_routes() "missing-route", vec![FrameworkRouteExpectation::new("express", "GET", "/missing")], ); - let error = qualify_framework_case( + let result = qualify_framework_case( &extraction, compass_languages::FrameworkLimits::default(), &case, - ) - .expect_err("a missing framework route must fail qualification"); + ); assert!(matches!( - error, - FrameworkQualificationError::MissingRoute { .. } + result, + Err(FrameworkQualificationError::MissingRoute { .. }) )); Ok(()) } diff --git a/crates/compass-resolve/tests/framework_routes.rs b/crates/compass-resolve/tests/framework_routes.rs index 6349219b2..baa4b8c78 100644 --- a/crates/compass-resolve/tests/framework_routes.rs +++ b/crates/compass-resolve/tests/framework_routes.rs @@ -74,6 +74,93 @@ fn route(handler: &str) -> RawRouteFact { } } +#[test] +fn programmatic_routes_do_not_inherit_filesystem_parentage() +-> Result<(), Box> { + for framework in ["chi", "express", "flask", "axum", "react-router", "next"] { + let facts = ["tests/a.go", "tests/b.go"].map(|file| { + let mut fact = route("handler"); + fact.framework = framework.to_owned(); + fact.declaring_scope = "r".to_owned(); + fact.anchor.source_file = file.to_owned(); + fact.rule = Some(format!("{framework}-router-call")); + RawFrameworkFact::Route(fact) + }); + for facts in [facts.to_vec(), facts.into_iter().rev().collect()] { + let mut extraction = Extraction { + framework_facts: facts, + ..Extraction::default() + }; + let resolved = + resolve_and_publish_framework_routes(&mut extraction, FrameworkLimits::default())?; + assert_eq!(resolved.len(), 2); + assert_eq!(extraction.nodes.len(), 2); + assert!( + extraction + .edges + .iter() + .all(|edge| edge.string("relation") != "contains"), + "{framework}: independent receiver names are not filesystem route parents" + ); + } + } + Ok(()) +} + +#[test] +fn filesystem_parentage_requires_a_recognized_convention() -> Result<(), Box> +{ + for (framework, origin, rule, expected) in [ + ( + "next", + RawFrameworkOrigin::Convention, + "next-app-router-convention", + 1, + ), + ( + "next", + RawFrameworkOrigin::Ast, + "next-app-router-convention", + 0, + ), + ("next", RawFrameworkOrigin::Convention, "unknown-rule", 0), + ( + "chi", + RawFrameworkOrigin::Convention, + "next-app-router-convention", + 0, + ), + ] { + let facts = ["app/layout.tsx", "app/blog/page.tsx"].map(|file| { + let mut fact = route("Page"); + fact.framework = framework.to_owned(); + fact.operation = "PAGE".to_owned(); + fact.anchor.source_file = file.to_owned(); + fact.origin = origin; + fact.rule = Some(rule.to_owned()); + RawFrameworkFact::Route(fact) + }); + let mut extraction = Extraction { + framework_facts: facts.to_vec(), + ..Extraction::default() + }; + resolve_and_publish_framework_routes(&mut extraction, FrameworkLimits::default())?; + let hierarchy: Vec<_> = extraction + .edges + .iter() + .filter(|edge| edge.string("relation") == "contains") + .collect(); + assert_eq!(hierarchy.len(), expected, "{framework} {origin:?} {rule}"); + if let Some(edge) = hierarchy.first() { + assert_eq!(edge.string("source_file"), "app/blog/page.tsx"); + assert_eq!(edge.string("rule"), "framework-route-hierarchy"); + let parent = extraction.nodes.iter().find(|node| node.id == edge.source); + assert!(parent.is_some_and(|node| node.string("source_file") == "app/layout.tsx")); + } + } + Ok(()) +} + #[test] fn neutral_framework_roles_publish_existing_node_roles_and_reject_unknown_values() -> Result<(), Box> { diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 3f0a33724..a051dcc3f 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1235,11 +1235,139 @@ Final-source native checks pass **1,410 tests, zero failed, two ignored**: workspace library/binary tests plus `universal_evidence`, `universal_resolution`, `contracts`, and `compass_product`. Workspace and the same integration Clippy selection pass with warnings denied. All **95 Python tests**, formatting, -product boundary, and diff checks pass. Qualification is still running; a full -fixture-gate pass is not yet claimed. Logs use `go-initializer-native-03`, +product boundary, and diff checks pass. Both full fixture qualification runs +completed with exit zero, including semantic, topology, Markdown, and React +fixture checks. The second run started after the final Go production edit. +These gates do not cover the subsequently discovered Chi route-parent defect +below. Logs use `go-initializer-qualification-02`, `go-initializer-native-03`, `go-initializer-clippy-02`, and `go-initializer-python-02` prefixes. Existing core unused-mut and macOS linker warnings remain in build/test logs. +### Panel A MCP extension: communities, neighbors, and hubs + +Commit `3ec3732a` registered 60 MCP requests before executing them on the original +panel-A graphs and frozen hub-evidence Compass binary. This is a **development +extension after observing CLI output**, not a new independent confirmation panel. +It uses the separate Graphify MCP environment, checking its recorded package +files before and after execution. This does not retroactively attest the CLI +extraction environment. All 60 expected requests completed; raw transcripts, +input digests, and the exact question multiset were checked. + +| Stored-graph diagnostic | Compass | Graphify | +| --- | ---: | ---: | +| Counts match | 5/5 | 5/5 | +| Complete largest-community membership | 5/5 | 5/5 | +| Missing community handled | 5/5 | 5/5 | +| Selected call-neighbor label/direction triples match | 5/5 | 5/5 | +| Ambiguous name remains ambiguous | 5/5 | 4/5 | +| Returned hub identity established | 50/50 | 41/50 | +| Direct hub connectivity summary matches | 50/50 | unavailable | + +Graphify silently selects the Chi `Context.URLParam` method for `URLParam`, +despite the separate top-level declaration. It preserves the other four +ambiguities. Compass preserves all five, but substring matching produces broad +candidate lists, including documentation nodes; this is not a candidate-precision +win. Neighbor consistency also does not establish source-edge correctness: +Compass text omits call-site occurrences, while Graphify prints retained sites. +The frozen Chi graph still has the already-documented receiver miss. + +Largest-community sizes are Chi **96/108**, Click **389/84**, jsoup **752/295**, +Redux **271/71**, and WalkDir **116/47** (Compass/Graphify). These are different +partitions on different extracted graphs, not shared correctness denominators. +The reviewed file distributions include many tests: Compass Redux's largest +community includes 156 nodes from `test/createStore.spec.ts`; Graphify WalkDir's +includes 45 from `src/tests/recursive.rs`. Neither fact alone establishes good +or bad functional cohesion. + +The complete post-output hub-role census verifies 50 Compass and 41 Graphify +source identities; nine Graphify display identities remain unresolved. Compass +uses explicit MCP IDs here; Graphify's CLI JSON can supply IDs, so the nine +unknowns are specific to this MCP display, not missing graph identities. Compass +returns 22 production types, seven production callables, eight test helpers, +two test types, five test modules, two source modules, two documentation-tooling +callables, and two route records from a test/example. The different returned +sets cannot establish a shared design-quality score. The roles, source excerpts, +file hashes, and all unknowns are preserved in `hub_role_reviews_panel_a.json`. + +Full answer text totals **125,033/42,550 bytes**, with **174,128/45,989 actual +response-wire bytes**. Hub responses alone total **24,401/1,473 text bytes** and +**69,504/1,973 wire bytes**. Compass supplies more metadata and different members; +these costs do not prove greater efficiency. Concurrent compilation excludes +latency claims. `mcp_panel_a_review.json` records all denominators and artifact +digests. The collector now accepts safely named repositories from the captured +run instead of a hard-coded five-name list; all **97 benchmark tests** pass, +including duplicate-record and path-escape regressions. + +### Source defect exposed by a consistent hub summary + +Compass's second Chi hub is the route `GET /users/1` inside `TestCleanPath`, +with degree **59** from **68 outgoing containment records** and no other +incident relationship kinds. Its graph-consistent summary faithfully exposes +unsupported hierarchy. For example, it claims to contain `GET /` inside +`TestThrottleBacklog`, although both tests independently construct a local +`r := chi.NewRouter()` and neither mounts the other's route. + +The separately recorded negative witness fails on both the original panel +graph and the newer Go-corrected graph. Graphify has no matching route endpoint +identities, so it cannot receive credit for the negative. The source review +traced the defect to shared publication applying filesystem parent selection +to programmatic receivers named `r`. This can inflate hubs and connect unrelated +tests; passing self-graph consistency and existing fixture gates did not detect +it. The recorded real-source negative is not a population precision estimate. + +A native regression reproduced the defect. Publication now admits only +recognized filesystem-convention facts to that parent-selection step; +programmatic mounts/groups remain owned by framework composition rules. Tests +cover six programmatic frameworks, input-order reversal, a positive filesystem +case, and mismatched origin/rule/framework negatives. All **17 framework-route +tests** pass. Framework-pack semantics advance from 6 to 7 and build-state seals +include that identity. Existing filesystem conventions still +need broader independent semantic review; this correction is not proof of their +complete correctness. + +#### Fresh development comparison after the hierarchy correction + +The frozen `route-hierarchy-provenance` binary has SHA256 +`872aff05f6c1723d27ee96230a4e315d3f0d6e1849387de09530b907f0ff3f40`. +`route-hierarchy-panel-a-01` builds both tools afresh on all five pinned sources +and repeats every original question. All **110 requests ran**, with zero +timeouts; nonzero exits remain in the denominator. Text scores remain **46/55 +for both**, with no changed pass/fail rows. Source-path matches remain **6/10 +versus 8/10**. Corrected selected-pair relationship matches are **16/21 versus +20/21**, and full occurrence agreement is **16/21 versus 17/21**. Compass's +one-pair improvement over the original panel comes from the earlier Go receiver +fix, not the hierarchy correction. Original and corrected Click witnesses are +still separate. + +A complete relationship delta against the Go-corrected Chi graph removes +**88 `contains` records**, adds none, and retains all 729 nodes. The other four +repositories have no relationship changes from the original panel. The recorded +independent-router negative now passes with both Compass endpoints present; +Graphify's endpoints remain unavailable. This is source-backed defect recovery, +not a new broad recall score. + +`mcp-panel-a-02` repeats all **60 MCP requests**, each successfully, against the +fresh graphs. All earlier consistency and ambiguity outcomes are retained. +Compass's two Chi route hubs disappear from the top ten, replaced by the +production type `compressResponseWriter` and test helper `bigMux`; both new +source roles were inspected. The other four hub rankings are unchanged. +`hub_role_reviews_panel_a_after_routes.json` retains all 100 rows and only reuses +earlier judgments after exact identity, anchor, excerpt, and file-hash equality. +Chi's selected largest Compass community changes from 96 to 90 nodes; complete +enumeration still does not prove functional cohesion. Compass/Graphify answer +text totals are **125,192/42,550 bytes**, with **174,698/45,989 wire bytes**. +No latency or efficiency win is claimed. + +Verification passes **1,431 native tests, zero failed, two ignored**, covering +workspace libraries/binaries and the selected universal-evidence, resolver, +cache/contracts, product, framework-route, and framework-qualification tests. +The first expanded Clippy invocation exposed an existing `expect_err` in a +qualification test. An explicit `Err(MissingRoute)` assertion replaced it; +all four qualification tests were rerun and the full selected Clippy invocation +then passed. All **97 benchmark tests**, formatting, diff, and product-boundary +checks pass. The new full fixture qualification is still pending; the previous +Go fixture pass does not verify this subsequent production change. + ## Next evidence to collect 1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact diff --git a/docs/reference/react-framework-graph.md b/docs/reference/react-framework-graph.md index ac9a7fa57..d7a248b07 100644 --- a/docs/reference/react-framework-graph.md +++ b/docs/reference/react-framework-graph.md @@ -117,6 +117,12 @@ component, or fabricates an external endpoint. A limit error is not an empty successful result. Every published relationship must retain a valid source range, bounded provenance, and a source path contained by the repository root. +Filesystem route-parent inference requires a recognized convention fact from +the owning file-route producer. Programmatic router variables such as `r` or +`app` do not establish a filesystem hierarchy across source files; their mounts +and groups require framework composition evidence. This boundary also applies +to programmatic registrations using a framework that supports file routes. + Generated files, symlinks that escape the owning root, malformed syntax, dynamic imports, computed configuration, and conditional values remain unsupported or incomplete unless a framework pack has independently qualified From 0c805b61c236febecab348a8ed405ba78476835d Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 19:41:06 -0700 Subject: [PATCH 28/97] test: reject unsupported route containment in production qualification --- .../agent_query/mcp_panel_a_review.json | 2 +- .../route_hierarchy_fixture_review.json | 555 ++++++++++++++++++ docs/design/code-graph-v1-qualification.md | 22 +- ...ode-graph-intelligence-audit-2026-09-26.md | 87 ++- scripts/check_code_graph_v1_coverage.py | 1 + scripts/code_graph_v1_oracle.py | 53 +- scripts/tests/test_code_graph_v1_oracle.py | 55 ++ .../qualification/code-graph-v1-semantic.json | 54 +- .../qualification/code-graph-v1-topology.json | 38 +- 9 files changed, 833 insertions(+), 34 deletions(-) create mode 100644 benchmarks/agent_query/route_hierarchy_fixture_review.json diff --git a/benchmarks/agent_query/mcp_panel_a_review.json b/benchmarks/agent_query/mcp_panel_a_review.json index 8856aa84f..b4adf64bb 100644 --- a/benchmarks/agent_query/mcp_panel_a_review.json +++ b/benchmarks/agent_query/mcp_panel_a_review.json @@ -114,7 +114,7 @@ "clippy": "passed on workspace lib/bin plus selected integrations; first run retained an existing expect_err lint failure, corrected with an explicit error assertion", "frameworkQualificationTestsRerunAfterLintCleanup": 4, "frameworkRoutesPassed": 17, - "fullFixtureQualification": "pending; previous Go fixture pass does not verify this new production change", + "fullFixtureQualification": "FAILED at the old topology count floor. Complete source review justifies 19 removed false containment edges; recalibrated count policy passes, but the new independent-route assertions find one remaining false filesystem parent. See route_hierarchy_fixture_review.json.", "nativeFailed": 0, "nativeIgnored": 2, "nativePassed": 1431 diff --git a/benchmarks/agent_query/route_hierarchy_fixture_review.json b/benchmarks/agent_query/route_hierarchy_fixture_review.json new file mode 100644 index 000000000..9d3e710b9 --- /dev/null +++ b/benchmarks/agent_query/route_hierarchy_fixture_review.json @@ -0,0 +1,555 @@ +{ + "schema": "compass.route-hierarchy-fixture-review/1", + "scope": "Post-output development audit prompted by the failed topology gate. This is source-reviewed negative containment evidence, not a community-quality improvement claim.", + "beforeBinarySha256": "bb09a5335c80fd7dd710e008b4a2f30f9403108e4883ed74e7d7c0de1b0aaca9", + "afterBinarySha256": "872aff05f6c1723d27ee96230a4e315d3f0d6e1849387de09530b907f0ff3f40", + "graphs": { + "before": "c5d0e63dda3dcec46c585bfc64906f43ea04edc0a5df05877294e8297b1d772c", + "after": "996ae5b52508a6b8b099624c0bb52163242d10e9a440a6d3a3a7203d3d2900d3" + }, + "sourceSha256": { + "fixtures/code-graph/frontend-react/src/routes/home.tsx": "4905d3a651b6ffb5fc16ede3834347c39d72bd599b417badce1f8e4f92145958", + "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx": "ea80d53e9689be9076843e4c85cfeb340361ee2e08462f67646d80b80b7c0c30", + "fixtures/code-graph/routes/jvm/SpringController.java": "11746a0979e1a02c71bab0621abc81167fcdd0fc08088c48f00222c47d6c5600", + "fixtures/code-graph/routes/jvm/SpringController.kt": "79081d811f27f6e6aa74f8130d6c781f3b0ed8ffd51c2be8d1a392c7f3b1b476", + "fixtures/code-graph/routes/php/drupal.module": "f4c01c09e78cc2a8e9fd1829c205a734964c30c0dedbf6193e1093cb9c98a654", + "fixtures/code-graph/routes/php/drupal.routing.yml": "609d4ce746dea221f0e2c0afff22be8a8d2e543e154c8afd228f3bcbdf6ca695", + "fixtures/code-graph/routes/python/fastapi_app.py": "56a7e6e730b3bccff2563e66cc631291988d767ec4f043fa537d266ff6d87e48", + "fixtures/code-graph/routes/python/fastapi_models.py": "09e845b782994eb4992cb5767e8af08d9bf6b09ee83904a2f96257017a3d804e", + "fixtures/code-graph/routes/python/flask_app.py": "1325c547854844ec8611d4489e0c644a6f89d452dae5691cff406a28e0740bb8", + "fixtures/code-graph/routes/python/flask_factory.py": "572095732ceff610d219898b919fedfb67d874134063ad410c8966cd568fc044", + "fixtures/code-graph/routes/typescript/express.ts": "79b18f7ac9df4a132f3e4547e6bf91ffe0e28910734a869b1e4aa07ed0c370e6", + "fixtures/code-graph/routes/typescript/owner-mismatch.ts": "e46dc1f0521c371f9db8d21d6673be9f56b650ea5dda4c7272de8eba8b0f5a58", + "fixtures/code-graph/routes/typescript/vue-router-qualified.ts": "5bd4ea29588c89bac3a8457230ce678f8f8cb2d0c9a9e3bbf888d5e1501c9a62", + "fixtures/code-graph/routes/typescript/vue-router.ts": "0b08f76e6b175cdb80aa999af1429b7fad096e4997903862606ce5aae7949106" + }, + "policyMethod": "For every existing bound, add exactly the observed metric change. Retain its previous margin; unchanged metrics and other relation thresholds remain unchanged. Add semantic negative assertions so restored false links cannot satisfy the adjusted floor.", + "policyChanges": [ + { + "bound": "minimums", + "metric": "communities", + "previous": 225, + "updated": 232, + "observedBefore": 225, + "observedAfter": 232 + }, + { + "bound": "minimums", + "metric": "edges", + "previous": 1284, + "updated": 1265, + "observedBefore": 1289, + "observedAfter": 1270 + }, + { + "bound": "minimums", + "metric": "exactCrossCommunityEdges", + "previous": 5, + "updated": 3, + "observedBefore": 5, + "observedAfter": 3 + }, + { + "bound": "minimums", + "metric": "exactCrossFileEdges", + "previous": 81, + "updated": 62, + "observedBefore": 81, + "observedAfter": 62 + }, + { + "bound": "minimums", + "metric": "exactCrossFileEdgesPerThousandNodes", + "previous": 63, + "updated": 48, + "observedBefore": 63, + "observedAfter": 48 + }, + { + "bound": "minimums", + "metric": "exactEdgeBearingNodePermille", + "previous": 711, + "updated": 709, + "observedBefore": 712, + "observedAfter": 710 + }, + { + "bound": "minimums", + "metric": "exactEdgeBearingNodes", + "previous": 908, + "updated": 905, + "observedBefore": 909, + "observedAfter": 906 + }, + { + "bound": "minimums", + "metric": "exactEdges", + "previous": 965, + "updated": 946, + "observedBefore": 970, + "observedAfter": 951 + }, + { + "bound": "minimums", + "metric": "exactUniqueTypedEndpointPairs", + "previous": 956, + "updated": 938, + "observedBefore": 961, + "observedAfter": 943 + }, + { + "bound": "minimums", + "metric": "exactUniqueTypedEndpointPairsPerThousandNodes", + "previous": 749, + "updated": 735, + "observedBefore": 753, + "observedAfter": 739 + }, + { + "bound": "minimums", + "metric": "uniqueTypedEndpointPairs", + "previous": 1261, + "updated": 1243, + "observedBefore": 1266, + "observedAfter": 1248 + }, + { + "bound": "maximums", + "metric": "communities", + "previous": 226, + "updated": 233, + "observedBefore": 225, + "observedAfter": 232 + }, + { + "bound": "maximums", + "metric": "connectedComponents", + "previous": 222, + "updated": 230, + "observedBefore": 221, + "observedAfter": 229 + }, + { + "bound": "maximums", + "metric": "exactConnectedComponents", + "previous": 496, + "updated": 504, + "observedBefore": 495, + "observedAfter": 503 + }, + { + "bound": "maximums", + "metric": "exactIsolatedNodes", + "previous": 368, + "updated": 371, + "observedBefore": 367, + "observedAfter": 370 + }, + { + "bound": "maximums", + "metric": "isolatedNodes", + "previous": 96, + "updated": 99, + "observedBefore": 95, + "observedAfter": 98 + }, + { + "bound": "maximums", + "metric": "singletonCommunities", + "previous": 96, + "updated": 99, + "observedBefore": 95, + "observedAfter": 98 + }, + { + "bound": "relationshipMinimums", + "relation": "contains", + "metric": "exactCrossFileEdges", + "previous": 31, + "updated": 12, + "observedBefore": 31, + "observedAfter": 12 + }, + { + "bound": "relationshipMinimums", + "relation": "contains", + "metric": "exactUniqueEndpointPairs", + "previous": 624, + "updated": 606, + "observedBefore": 624, + "observedAfter": 606 + } + ], + "nodeCountBefore": 1276, + "nodeCountAfter": 1276, + "removedEdges": 19, + "addedEdges": 0, + "reviewedFilePairs": [ + { + "id": "route-containment-independent-1", + "sourceFile": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "targetFile": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "reason": "The TanStack createFileRoute /tanstack and React Router /home belong to different route libraries. A shared source directory provides no cross-framework parentage." + }, + { + "id": "route-containment-independent-2", + "sourceFile": "fixtures/code-graph/routes/jvm/SpringController.kt", + "targetFile": "fixtures/code-graph/routes/jvm/SpringController.java", + "reason": "Independent annotated controllers in different packages declare /kotlin and /api mappings. The Kotlin method is not a containing route for Java endpoints." + }, + { + "id": "route-containment-independent-3", + "sourceFile": "fixtures/code-graph/routes/php/drupal.module", + "targetFile": "fixtures/code-graph/routes/php/drupal.routing.yml", + "reason": "The entity-type hook has an empty body. The YAML routes independently declare controllers and forms; the hook does not contain those routes." + }, + { + "id": "route-containment-independent-4", + "sourceFile": "fixtures/code-graph/routes/python/fastapi_models.py", + "targetFile": "fixtures/code-graph/routes/python/fastapi_app.py", + "reason": "The modules instantiate separate FastAPI apps. The models app does not include the other app or its router." + }, + { + "id": "route-containment-independent-5", + "sourceFile": "fixtures/code-graph/routes/python/flask_factory.py", + "targetFile": "fixtures/code-graph/routes/python/flask_app.py", + "reason": "The factory registers its own root and nested blueprints. flask_app.py creates a separate app and blueprint; no cross-module registration exists." + }, + { + "id": "route-containment-independent-6", + "sourceFile": "fixtures/code-graph/routes/typescript/owner-mismatch.ts", + "targetFile": "fixtures/code-graph/routes/typescript/express.ts", + "reason": "Each module creates its own Express app; neither imports or mounts the other. A route with an unresolved controller is not a parent router." + }, + { + "id": "route-containment-independent-7", + "sourceFile": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts", + "targetFile": "fixtures/code-graph/routes/typescript/vue-router.ts", + "reason": "Both modules independently call createRouter. Neither route array declares the other route as a child or imports the other router." + }, + { + "id": "route-containment-independent-8", + "sourceFile": "fixtures/code-graph/routes/typescript/vue-router.ts", + "targetFile": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts", + "reason": "Both modules independently call createRouter. Neither route array declares the other route as a child or imports the other router." + } + ], + "removed": [ + { + "id": "sha256:8a24a3e1586a437473ae82f930dae6ca39fd32ecf9600c2f252093a54c19e963", + "kind": "contains", + "source": { + "id": "sha256:31747cec456de803d757d886692e2e268de2569768044e867991b6ee5623f9a2", + "name": "HOOK /__hook/hook_entity_type_build", + "file": "fixtures/code-graph/routes/php/drupal.module" + }, + "target": { + "id": "sha256:ef45dc219f80b02c7241e501baff59ad91c3ea88b14f4369ace1d5eaf242fe81", + "name": "GET /examples/{example}", + "file": "fixtures/code-graph/routes/php/drupal.routing.yml" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:f4756f9a911e5e71c17d8afd492f0cf927a64958bc7aa4cadbf68d9cea1c4e6c", + "kind": "contains", + "source": { + "id": "sha256:31747cec456de803d757d886692e2e268de2569768044e867991b6ee5623f9a2", + "name": "HOOK /__hook/hook_entity_type_build", + "file": "fixtures/code-graph/routes/php/drupal.module" + }, + "target": { + "id": "sha256:983cc129463868a1905a68b07bab0e9d241d54c2ddd0b0e23937f7c219dd69ec", + "name": "POST /examples/{example}", + "file": "fixtures/code-graph/routes/php/drupal.routing.yml" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:ed771045b510289c247ca9a4014b0c5ff353852cdab247e2ccbb9d1e78bd65d8", + "kind": "contains", + "source": { + "id": "sha256:0a3207a7b24df9681cb70843ee162f7f951e83aeeb560ba0e42597f8891eacc0", + "name": "GET /owner-mismatch", + "file": "fixtures/code-graph/routes/typescript/owner-mismatch.ts" + }, + "target": { + "id": "sha256:d3678457e1f85c5c94c5a58afd5dd1c9cf9160609b234f8c535af9425370fa48", + "name": "GET /health", + "file": "fixtures/code-graph/routes/typescript/express.ts" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:f7da8b41b916fb876f33b65605294eae6cdce75f117185cf402d54756b98f572", + "kind": "contains", + "source": { + "id": "sha256:03b53164cd3261fbc51bc45b4a6c6d952cea02e26225fd800aa49bd27d50fe39", + "name": "GET /api/v2/nested/items", + "file": "fixtures/code-graph/routes/python/flask_factory.py" + }, + "target": { + "id": "sha256:2aed25182cf7d59918c8654ab2c78658ddcd329b1e03f5e52b975b51ba902bf0", + "name": "GET /health", + "file": "fixtures/code-graph/routes/python/flask_app.py" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:0b72aec3ee7a3f4bb8b19ff97729ba7fdd350c68202f68034e6fdf967c2611b2", + "kind": "contains", + "source": { + "id": "sha256:f5005321e07e17e404ea9bbba7343c4e60c7517cadc1592629a9c758ec86a85e", + "name": "POST /items", + "file": "fixtures/code-graph/routes/python/fastapi_models.py" + }, + "target": { + "id": "sha256:5f9ae35d36f7dd16a8d4d763c3f88816f77fe02acf3ff012a22ba36e5cb04d33", + "name": "GET /health", + "file": "fixtures/code-graph/routes/python/fastapi_app.py" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:d521141583f6cbc6c6543a685548d1e672915d16397dd55fd7d576af92125664", + "kind": "contains", + "source": { + "id": "sha256:0a3207a7b24df9681cb70843ee162f7f951e83aeeb560ba0e42597f8891eacc0", + "name": "GET /owner-mismatch", + "file": "fixtures/code-graph/routes/typescript/owner-mismatch.ts" + }, + "target": { + "id": "sha256:9725a055491222dc007332daecf4b701d91fd5327a179c86b14e9bc0e737a47c", + "name": "GET /users/{userId}", + "file": "fixtures/code-graph/routes/typescript/express.ts" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:0d87337f8f6e94e7b18f40974b6ce5fec3d58ca087236337eaa6c2d18fa6091b", + "kind": "contains", + "source": { + "id": "sha256:ad9386df4b43dc1f7852237db9c44e9eca9b86222d1bd11d595d8b66fd09a127", + "name": "PAGE /qualified-users/{userId}", + "file": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts" + }, + "target": { + "id": "sha256:e9ef96b35cfb02ccfdb5eea2ac4165446687a320b400e5de3e15061fb381eb8e", + "name": "PAGE /users/{userId}", + "file": "fixtures/code-graph/routes/typescript/vue-router.ts" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:4a5ee04f212621f5d0d5ff45691b3f1fe8368bbeda6da51dc34adce9e6a2bcf0", + "kind": "contains", + "source": { + "id": "sha256:31747cec456de803d757d886692e2e268de2569768044e867991b6ee5623f9a2", + "name": "HOOK /__hook/hook_entity_type_build", + "file": "fixtures/code-graph/routes/php/drupal.module" + }, + "target": { + "id": "sha256:9c9289ce2a8c82c04b345c1f452fcfd6a2ae268684f33a7fc9d4c1b13754f7bc", + "name": "ANY /examples/create", + "file": "fixtures/code-graph/routes/php/drupal.routing.yml" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:2a520b3863453c75deee6b54ab5e5aeb3ca09d49bf7b3f705e9fcfb8a5854818", + "kind": "contains", + "source": { + "id": "sha256:a9e69502554bfe653b41fe1185c4f6a448712e537b7fdbda5a6cf2a81f4787dd", + "name": "PAGE /tanstack", + "file": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx" + }, + "target": { + "id": "sha256:59be83b2ff81ea880e357d9d6ccd937e9dec254c2024007b77965f689a1f9eac", + "name": "PAGE /home", + "file": "fixtures/code-graph/frontend-react/src/routes/home.tsx" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:9917b3186cbd1dc5b45e4f44446a668e0ab89796e304898b78861dfe04d4b25f", + "kind": "contains", + "source": { + "id": "sha256:03b53164cd3261fbc51bc45b4a6c6d952cea02e26225fd800aa49bd27d50fe39", + "name": "GET /api/v2/nested/items", + "file": "fixtures/code-graph/routes/python/flask_factory.py" + }, + "target": { + "id": "sha256:a1ae0c1a00d2a542debb4c568d0900dfc453655cdff2292af9ec2468d182a481", + "name": "PATCH /v2/api/users/", + "file": "fixtures/code-graph/routes/python/flask_app.py" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:e5a10df87a64ab2084d736ba179ed87175c41f55f2f86e1c00cd2a7a46ece604", + "kind": "contains", + "source": { + "id": "sha256:03b53164cd3261fbc51bc45b4a6c6d952cea02e26225fd800aa49bd27d50fe39", + "name": "GET /api/v2/nested/items", + "file": "fixtures/code-graph/routes/python/flask_factory.py" + }, + "target": { + "id": "sha256:571a46cfb333dba3badf206a134af6845c6da92fa1f01e81ea1d1885853959d7", + "name": "GET /v2/api/users/", + "file": "fixtures/code-graph/routes/python/flask_app.py" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:46d7c9131d4c77f4e67b141ea7d7fa4d8a46ec0c09814c4a1075a678c8cb78f4", + "kind": "contains", + "source": { + "id": "sha256:e9ef96b35cfb02ccfdb5eea2ac4165446687a320b400e5de3e15061fb381eb8e", + "name": "PAGE /users/{userId}", + "file": "fixtures/code-graph/routes/typescript/vue-router.ts" + }, + "target": { + "id": "sha256:ad9386df4b43dc1f7852237db9c44e9eca9b86222d1bd11d595d8b66fd09a127", + "name": "PAGE /qualified-users/{userId}", + "file": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:25335c228bf1e7ae398a3185da1cb9717a3af24b10a3c53c3fc39d92e13d148b", + "kind": "contains", + "source": { + "id": "sha256:f5005321e07e17e404ea9bbba7343c4e60c7517cadc1592629a9c758ec86a85e", + "name": "POST /items", + "file": "fixtures/code-graph/routes/python/fastapi_models.py" + }, + "target": { + "id": "sha256:97e97c6afc09c04d4cbdd9e2ccfd59960853d42b5b727f2a66bb7792b766da6e", + "name": "POST /api/v1/users", + "file": "fixtures/code-graph/routes/python/fastapi_app.py" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:7291c23bc98246fd89978abd495625a22b141c6a878fa19ef55b060a5e229546", + "kind": "contains", + "source": { + "id": "sha256:0a3207a7b24df9681cb70843ee162f7f951e83aeeb560ba0e42597f8891eacc0", + "name": "GET /owner-mismatch", + "file": "fixtures/code-graph/routes/typescript/owner-mismatch.ts" + }, + "target": { + "id": "sha256:6dec9ec30252fed6272d1ec2500ce7f821bd3c882e42f0ebeef763c73dcda24c", + "name": "GET /inline", + "file": "fixtures/code-graph/routes/typescript/express.ts" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:9c0c7c7d6f16dd92e67e1b6a9b2d776d3f62632ceb9637605e70c1ad76dc08dc", + "kind": "contains", + "source": { + "id": "sha256:31747cec456de803d757d886692e2e268de2569768044e867991b6ee5623f9a2", + "name": "HOOK /__hook/hook_entity_type_build", + "file": "fixtures/code-graph/routes/php/drupal.module" + }, + "target": { + "id": "sha256:842f5ecf5ecdd18d4f5e6be3bb562b9930a541f6f1664eeabcc1eda94ac3a068", + "name": "ANY /examples/{example}/edit", + "file": "fixtures/code-graph/routes/php/drupal.routing.yml" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:c1a5582dc295b28dad519dceb9b12124e4607f5c372d3c84fa4a4f5c4d169e1c", + "kind": "contains", + "source": { + "id": "sha256:5a135bd0dce2b32b9893e4e454069aad0a7d673dfd8352b5762ce60a30aa7f68", + "name": "GET /kotlin/users/{id}", + "file": "fixtures/code-graph/routes/jvm/SpringController.kt" + }, + "target": { + "id": "sha256:d5eb9743f89a117f10c3b05b70d04e113f49716297269172538bed063779d3ee", + "name": "GET /api/users/{id}", + "file": "fixtures/code-graph/routes/jvm/SpringController.java" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:3bb04f97c57f0b1e72ff67e66c0fd11c1f4865c209e2fdf88ec5d85275acac29", + "kind": "contains", + "source": { + "id": "sha256:5a135bd0dce2b32b9893e4e454069aad0a7d673dfd8352b5762ce60a30aa7f68", + "name": "GET /kotlin/users/{id}", + "file": "fixtures/code-graph/routes/jvm/SpringController.kt" + }, + "target": { + "id": "sha256:48fe0e2047b3271abf3c55299e0892712c5e60d3991ed2ce39eabda335bdbcbf", + "name": "POST /api/users", + "file": "fixtures/code-graph/routes/jvm/SpringController.java" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:42aae14c6e91598ff3f885035583e8c91f12d0a670f90962a9b389d37e4c4e75", + "kind": "contains", + "source": { + "id": "sha256:5a135bd0dce2b32b9893e4e454069aad0a7d673dfd8352b5762ce60a30aa7f68", + "name": "GET /kotlin/users/{id}", + "file": "fixtures/code-graph/routes/jvm/SpringController.kt" + }, + "target": { + "id": "sha256:3f9b66f884632cdbdd79e3fe407331dc72e9a0c3d412a32ae86b1004ca193bba", + "name": "GET /api/search", + "file": "fixtures/code-graph/routes/jvm/SpringController.java" + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:8b5755396dd447c253358de8f724c098c181f00b741a21118e5ac594536266d2", + "kind": "contains", + "source": { + "id": "sha256:5a135bd0dce2b32b9893e4e454069aad0a7d673dfd8352b5762ce60a30aa7f68", + "name": "GET /kotlin/users/{id}", + "file": "fixtures/code-graph/routes/jvm/SpringController.kt" + }, + "target": { + "id": "sha256:b651ac57625fe598aba29f95645bf2289ef559e6d9bd15fa79e0eda464ad1aac", + "name": "POST /api/search", + "file": "fixtures/code-graph/routes/jvm/SpringController.java" + }, + "rule": "framework-route-hierarchy" + } + ], + "verification": { + "nodesUnchangedExceptCommunity": true, + "fixtureSourceHashesMatch": true, + "beforeNegativeGroupsRejected": 8, + "afterNegativeGroupsPassed": 7, + "afterNegativeGroupsFailed": 1, + "syntheticOracleMutationRestorationsRejected": 20, + "fullGate": "FAILED at the original topology floor. After recalibration, direct semantic qualification still FAILS at the new independent-route negative. No full rerun is claimed.", + "scriptTestsPassed": 87, + "benchmarkTestsPassed": 97, + "binaryHashesVerified": true + }, + "remainingDefect": { + "edgeId": "sha256:be0806a621daff555e8e36c37ba605edfbf50402e8d893ab6360c06feb84182c", + "sourceFile": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "sourceQualifiedName": "react-router::PAGE::/tanstack", + "targetFile": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "targetQualifiedName": "react-router::PAGE::/home", + "reason": "The two flat route modules are siblings. No source declaration makes /tanstack a parent of /home. The convention route is selected by first-other-file directory order, then a ghost endpoint is remapped to the AST route.", + "oracleLimitation": "scripts/react_frontend_source_oracle.mjs routeParent also chooses the first other module in the directory. Agreement with that oracle cannot independently validate this hierarchy.", + "nextAction": "Replace first-file parent selection with source-proven framework-specific parent semantics and independently specified positive/negative fixtures. Keep this semantic gate failing until production removes the unsupported link." + }, + "artifactSha256": { + "route-hierarchy-qualification-01.log": "fb880d5c10aca15aaff2762d35cc09a70f3c362aaf8bd428f8b27454eedea74f", + "route-hierarchy-fixture-delta/delta.json": "b98afa7cc1da425f212385908b70bcdc4cce1afe1a915df973081d43575915e5", + "route-hierarchy-fixture-delta/negative-mutation-review.json": "256513d62837ed8732c85eb616c0c1b473270eeb974c16862cfbf1130bd70f6c", + "route-hierarchy-scripts-tests-02.log": "b28b3dfe94e84e839b1fcb95551d01fe25e4a5e08eaa47b050d8fabad308d4c0", + "route-hierarchy-benchmark-tests-03.log": "5be8ffa2d27722ed8412725739af389097cb2feaad59fa212f1dc1a41f0a06ea", + "route-hierarchy-fixture-delta/semantic-after.capture.json": "35530c742eb9587126ea5bfb452c355294314579d6b9b67e334256bd779c8fb5", + "route-hierarchy-fixture-delta/topology-after.json": "c856007a49d188476c316ca8a4fab5a4a383cd0dee4290a47725b0c97c13d12c" + } +} diff --git a/docs/design/code-graph-v1-qualification.md b/docs/design/code-graph-v1-qualification.md index 5a9a9c827..731183034 100644 --- a/docs/design/code-graph-v1-qualification.md +++ b/docs/design/code-graph-v1-qualification.md @@ -1,7 +1,7 @@ # Compass code graph v1 qualification Status: executable release gate -Contracts: `compass.graph/1` and `compass.code-graph-qualification/2` +Contracts: `compass.graph/1` and `compass.code-graph-qualification/3` ## Release claim @@ -15,6 +15,8 @@ The checked-in manifests require: family, including route operation, normalized path, source, handler kind, handler language, relationship stage, resolution, and provenance; - executable near-match negatives that must not publish exact framework routes; +- source-reviewed independent route groups that must retain route nodes in + both files while publishing no containment from the first group to the second; - an observed production producer for every one of the 45 node kinds and 28 edge kinds; - an inventoried file, language, and current extractor version for every @@ -31,6 +33,24 @@ compatibility, source bounds, known producers, direct and heuristic provenance, candidate bounds, external-placeholder scope, deferred placeholder wiring, non-recursive self-loops, and unresolved global hubs. +Manifest version 3 adds required `routeContainmentNegatives`. Each entry names +two source files and records the source-based reason for their independence. +Both files must have route nodes; a missing endpoint is a failure, not an absence +success. The assertion forbids directed `contains` edges regardless of claimed +confidence. The graph and summary schema majors are unchanged. + +The September 26 hierarchy correction removed 19 false containment records from +the qualification fixture graph. The topology policy was recalibrated by adding +each measured before/after metric delta to its existing bound, retaining the +previous margin. Counts alone do not validate edge meaning or community quality. +The [fixture review](../../benchmarks/agent_query/route_hierarchy_fixture_review.json) +records all removed identities, source hashes, and policy changes; eight new +semantic negatives reject the former links independently of those counts. +The current route-hierarchy checkpoint still fails one of these negatives: +the directory-order lookup makes sibling `/tanstack` and `/home` modules a +parent-child pair. This is an unresolved production defect; the recalibrated +topology counts do not constitute a qualification pass. + ## Command Run the complete offline fixture gate from the repository root: diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index a051dcc3f..9ad3c805b 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1365,25 +1365,98 @@ The first expanded Clippy invocation exposed an existing `expect_err` in a qualification test. An explicit `Err(MissingRoute)` assertion replaced it; all four qualification tests were rerun and the full selected Clippy invocation then passed. All **97 benchmark tests**, formatting, diff, and product-boundary -checks pass. The new full fixture qualification is still pending; the previous -Go fixture pass does not verify this subsequent production change. +checks pass. The subsequent full fixture qualification **failed** at its topology +floor: 1,270 edges versus a required 1,284. The previous Go fixture pass does not +verify this production change. The follow-up below retains that failure and +finds an additional semantic defect. + +#### Fixture topology review and a remaining false filesystem parent + +The full run passed its native scale checks, source-integrity and repeated-build +comparisons, and existing semantic assertions before stopping at the count +floor. It did not reach the subsequent Markdown and React qualification stages. +Fresh clustered production builds of the identical fixture corpus with the +frozen Go-receiver and route-hierarchy binaries isolate exactly **19 removed +`contains` records, zero added records**, and the same **1,276 nodes**. Node +contents are unchanged except for community assignments. The removed records +represent 18 unique typed endpoint pairs. + +Source inspection covers every removed relationship: + +| Independent fixture groups | Removed records | +| --- | ---: | +| Drupal entity-type hook and YAML routes | 4 | +| Express apps in separate modules | 3 | +| Flask factory and separately instantiated app | 3 | +| Independently instantiated FastAPI apps | 2 | +| Vue Router route arrays in separate modules, both directions | 2 | +| Kotlin and Java Spring controllers in separate packages | 4 | +| TanStack and React Router route modules | 1 | + +The [complete review](../../benchmarks/agent_query/route_hierarchy_fixture_review.json) +records every removed identity, both binary and graph hashes, source hashes, +and every policy adjustment. Each count bound changes by exactly its measured +before/after delta, retaining its previous margin. Community count changes +from 225 to 232 and component count from 221 to 229; neither change establishes +better functional cohesion. + +Count recalibration alone is insufficient. Qualification manifest version 3 +adds eight source-reviewed independent-route assertions. Both endpoint files +must retain route nodes; the specified directed containment must be absent at +every confidence level. The old graph fails all eight groups. The corrected +production graph passes **seven and fails one**: a convention-derived +`react-router::PAGE::/tanstack` still contains `/home`, with the child endpoint +remapped to the AST route. These flat source modules do not declare that +parent-child relationship. + +The remaining resolver helper selects the first other route module found in a +nearby directory. The frontend source oracle's `routeParent` helper uses the +same rule. Their agreement therefore cannot independently validate framework +parentage. **The semantic gate remains failing**; no successful full rerun is +claimed. Fixing this requires framework-specific, source-proven parent rules +and independently specified positive and negative fixtures. + +All **87 Python script tests** pass, including 23 code-graph oracle tests. On a +synthetic graph with the remaining false edge removed solely for oracle +validation, restoring each of the 20 known false records individually fails +the new assertion. This mutation exercise validates the checker; it is not a +production correction. Existing native results describe the unchanged Rust +production code, not a fix for this newly exposed defect. + +#### Java constructor receiver diagnostic + +A separate frozen-binary diagnostic reproduces the jsoup constructor receiver +gap in direct, parenthesized, fully qualified, and overloaded method calls. +Anonymous-class and chained-result negatives remain unresolved. A native +resolver regression also fails because the direct method candidate lacks +`lib.Cleaner::check` as its receiver constraint. Its source and failure log are +retained in `java-constructor-receiver-diagnostic` and +`java-constructor-receiver-test-01.log`; the test is not installed as a passing +repository test, and no Java production change or score improvement is claimed. +The first ad hoc graph review used the raw-extraction `relation` key instead +of graph-v1 `kind`; `before-review-corrected.json` records the corrected review. +The native evidence and corrected graph inspection agree on the missing calls. ## Next evidence to collect -1. Extend source-proven loop/result/iterator inference to recover the fd callees miss. Keep exact +1. Correct filesystem route-parent selection and its non-independent frontend + oracle; keep the new semantic negative failing until the false parent is + removed by production behavior. Then rerun the full qualification gate. +2. Extend source-proven constructor, loop/result/iterator inference to recover + the jsoup and fd misses. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. -2. Use the source-role census and connectivity breakdowns to review actual +3. Use the source-role census and connectivity breakdowns to review actual responsibilities and source-edge correctness, including containment-heavy modules and generic reference targets. Evaluate cluster responsibilities and cross-community connections separately from graph consistency. -3. Extend the development navigation-path judgments to directed call paths, +4. Extend the development navigation-path judgments to directed call paths, longer walks, parallel source occurrences, broader ambiguity/unreachable cases, and real-repository work exhaustion. A negative or limit outcome must never count as a path or proof of global disconnection. -4. Use held-out repositories/questions and publish all failures, including +5. Use held-out repositories/questions and publish all failures, including competitor wins. Separate extraction gaps, resolution gaps, retrieval gaps, rendering gaps and oracle mistakes using actual source evidence. -5. Improve the owning production layer for reproduced failures, retain native +6. Improve the owning production layer for reproduced failures, retain native regressions, then rerun equivalent questions. Report category-level evidence and uncertainty rather than claiming universal dominance. diff --git a/scripts/check_code_graph_v1_coverage.py b/scripts/check_code_graph_v1_coverage.py index 7fe028c59..4f86d67b2 100755 --- a/scripts/check_code_graph_v1_coverage.py +++ b/scripts/check_code_graph_v1_coverage.py @@ -88,6 +88,7 @@ def main() -> int: "fixtureManifestFingerprint": fingerprint, "flows": len(manifest["flows"]), "negatives": len(manifest["negatives"]), + "routeContainmentNegatives": len(manifest["routeContainmentNegatives"]), "nodeKinds": len(manifest["nodeProducers"]), "edgeKinds": len(manifest["edgeProducers"]), "languages": len(manifest["_languageExpectations"]), diff --git a/scripts/code_graph_v1_oracle.py b/scripts/code_graph_v1_oracle.py index 6c0b36b85..6c5c77ec0 100755 --- a/scripts/code_graph_v1_oracle.py +++ b/scripts/code_graph_v1_oracle.py @@ -11,9 +11,9 @@ from typing import Any, Iterable # The graph wire contract remains compass.graph/1. This expectation schema is -# independently versioned so adding frontend vocabulary cannot make an older -# oracle silently accept a newer manifest. -SCHEMA = "compass.code-graph-qualification/2" +# independently versioned so an older oracle cannot silently omit newly +# required semantic assertions. +SCHEMA = "compass.code-graph-qualification/3" GRAPH_SCHEMA = "compass.graph/1" TOPOLOGY_POLICY_SCHEMA = "compass.code-graph-topology-policy/1" TOPOLOGY_REPORT_SCHEMA = "compass.code-graph-topology-report/1" @@ -163,7 +163,7 @@ def load_manifest( fail("manifest_schema", str(path), f"expected {SCHEMA}") allowed = { "schema", "flows", "negatives", "nodeProducers", "edgeProducers", - "languages", "occurrences", "coverage", "limits", + "languages", "occurrences", "coverage", "limits", "routeContainmentNegatives", } unknown = sorted(set(manifest) - allowed) if unknown: @@ -217,6 +217,28 @@ def load_manifest( _unique_id(ids, identity) _source_exists(fixture_root, item["source"], identity, declared_sources) + containment_fields = {"id", "sourceFile", "targetFile", "reason"} + containment_selectors: set[tuple[str, str]] = set() + if not isinstance(manifest["routeContainmentNegatives"], list): + fail("manifest_route_containment", str(path), "expectations must be a list") + for item in manifest["routeContainmentNegatives"]: + if not isinstance(item, dict): + fail("manifest_route_containment", str(path), "each expectation must be an object") + identity = str(item.get("id", "")) + _require(item, containment_fields, identity) + if set(item) != containment_fields: + fail("manifest_unknown_field", identity, "route containment fields differ from contract") + for key in sorted(containment_fields): + if not isinstance(item[key], str) or not item[key].strip(): + fail("manifest_route_containment", identity, f"{key} must be a nonempty string") + _unique_id(ids, identity) + selector = (item["sourceFile"], item["targetFile"]) + if selector in containment_selectors or selector[0] == selector[1]: + fail("manifest_route_containment", identity, "duplicate or same-file selector") + containment_selectors.add(selector) + for source in selector: + _source_exists(fixture_root, source, identity, declared_sources) + producer_fields = { "id", "kind", "source", "qualifiedName", "producer", "origins", "detailType", @@ -1065,6 +1087,28 @@ def assert_negatives(graph: dict[str, Any], manifest: dict[str, Any]) -> dict[st return {"negatives": count} +def assert_route_containment_negatives(graph: dict[str, Any], manifest: dict[str, Any]) -> dict[str, int]: + routes_by_file: dict[str, set[str]] = defaultdict(set) + for node in graph["nodes"]: + if node.get("kind") == "route": + routes_by_file[(node.get("source") or {}).get("file", "")].add(node["id"]) + passed = 0 + for item in manifest["routeContainmentNegatives"]: + source_ids = routes_by_file[item["sourceFile"]] + target_ids = routes_by_file[item["targetFile"]] + if not source_ids or not target_ids: + fail("route_containment_missing_endpoint", item["id"], "both files must retain route nodes") + forbidden = [ + edge["id"] for edge in graph["links"] + if edge["kind"] == "contains" + and edge["source"] in source_ids and edge["target"] in target_ids + ] + if forbidden: + fail("route_containment_negative", item["id"], f"unsupported edges {sorted(forbidden)}") + passed += 1 + return {"route_containment_negatives": passed} + + def assert_vocabulary(graph: dict[str, Any], manifest: dict[str, Any]) -> dict[str, int]: counts = {} for group, records, key in ( @@ -1192,6 +1236,7 @@ def qualify_graph(graph: dict[str, Any], manifest: dict[str, Any], fixture_root: summary: dict[str, Any] = {} summary.update(assert_flows(graph, manifest, fixture_root)) summary.update(assert_negatives(graph, manifest)) + summary.update(assert_route_containment_negatives(graph, manifest)) summary.update(assert_vocabulary(graph, manifest)) summary.update(assert_languages(graph, manifest)) summary.update(assert_occurrences(graph, manifest)) diff --git a/scripts/tests/test_code_graph_v1_oracle.py b/scripts/tests/test_code_graph_v1_oracle.py index 54401a328..a6f1c8b39 100644 --- a/scripts/tests/test_code_graph_v1_oracle.py +++ b/scripts/tests/test_code_graph_v1_oracle.py @@ -18,6 +18,7 @@ assert_coverage, assert_flows, assert_negatives, + assert_route_containment_negatives, assert_topology, canonical_bytes, endpoint_allowed, @@ -248,6 +249,60 @@ def test_coverage_rejects_false_complete(self) -> None: with self.assertRaisesRegex(QualificationError, "false_coverage"): assert_coverage(graph, manifest) + def test_route_containment_negatives_require_present_endpoints_and_direction(self) -> None: + source, target = node("route:a", "route"), node("route:b", "route") + source["source"]["file"] = "a.py" + target["source"]["file"] = "b.py" + manifest = {"routeContainmentNegatives": [{ + "id": "independent", "sourceFile": "a.py", "targetFile": "b.py", + "reason": "Independent apps; neither imports or mounts the other.", + }]} + graph = self.graph([source, target]) + self.assertEqual(assert_route_containment_negatives(graph, manifest), { + "route_containment_negatives": 1, + }) + for endpoints in ([source], [target], []): + with self.subTest(endpoints=endpoints), self.assertRaisesRegex( + QualificationError, "route_containment_missing_endpoint" + ): + assert_route_containment_negatives(self.graph(endpoints), manifest) + edge = {"id": "false-parent", "kind": "contains", "source": source["id"], "target": target["id"]} + with self.assertRaisesRegex(QualificationError, "route_containment_negative"): + assert_route_containment_negatives(self.graph([source, target], [edge]), manifest) + edge["source"], edge["target"] = edge["target"], edge["source"] + assert_route_containment_negatives(self.graph([source, target], [edge]), manifest) + edge["source"], edge["target"] = edge["target"], edge["source"] + edge["kind"] = "references" + assert_route_containment_negatives(self.graph([source, target], [edge]), manifest) + + def test_route_containment_manifest_rejects_duplicate_or_unexplained_pairs(self) -> None: + declared_sources = { + item["path"] for item in load_json(ROOT / "tests/qualification/code-graph-v1-corpus.json")["files"] + } + load_manifest(ROOT / "tests/qualification/code-graph-v1-semantic.json", ROOT, declared_sources) + for mutation in ("duplicate", "empty_reason", "unknown_field", "same_file", "non_list", "non_object"): + with self.subTest(mutation=mutation), tempfile.TemporaryDirectory() as directory: + manifest = copy.deepcopy(self.manifest) + item = manifest["routeContainmentNegatives"][0] + if mutation == "duplicate": + duplicate = dict(item, id="different-id-same-pair") + manifest["routeContainmentNegatives"].append(duplicate) + elif mutation == "empty_reason": + item["reason"] = "" + elif mutation == "unknown_field": + item["typo"] = "" + elif mutation == "non_list": + manifest["routeContainmentNegatives"] = None + elif mutation == "non_object": + manifest["routeContainmentNegatives"] = [None] + else: + item["targetFile"] = item["sourceFile"] + path = Path(directory) / "manifest.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + expected_error = "manifest_unknown_field" if mutation == "unknown_field" else "manifest_route_containment" + with self.assertRaisesRegex(QualificationError, expected_error): + load_manifest(path, ROOT, declared_sources) + def test_topology_separates_occurrences_from_unique_typed_connections(self) -> None: first = node("function:first", "function") second = node("function:second", "function") diff --git a/tests/qualification/code-graph-v1-semantic.json b/tests/qualification/code-graph-v1-semantic.json index 6df40d541..2e39e9bdd 100644 --- a/tests/qualification/code-graph-v1-semantic.json +++ b/tests/qualification/code-graph-v1-semantic.json @@ -1,5 +1,5 @@ { - "schema": "compass.code-graph-qualification/2", + "schema": "compass.code-graph-qualification/3", "flows": [ { "id": "flow-django", @@ -1798,5 +1798,55 @@ ], "limits": { "maxDiagnostics": 64 - } + }, + "routeContainmentNegatives": [ + { + "id": "route-containment-independent-1", + "sourceFile": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "targetFile": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "reason": "The TanStack createFileRoute /tanstack and React Router /home belong to different route libraries. A shared source directory provides no cross-framework parentage." + }, + { + "id": "route-containment-independent-2", + "sourceFile": "fixtures/code-graph/routes/jvm/SpringController.kt", + "targetFile": "fixtures/code-graph/routes/jvm/SpringController.java", + "reason": "Independent annotated controllers in different packages declare /kotlin and /api mappings. The Kotlin method is not a containing route for Java endpoints." + }, + { + "id": "route-containment-independent-3", + "sourceFile": "fixtures/code-graph/routes/php/drupal.module", + "targetFile": "fixtures/code-graph/routes/php/drupal.routing.yml", + "reason": "The entity-type hook has an empty body. The YAML routes independently declare controllers and forms; the hook does not contain those routes." + }, + { + "id": "route-containment-independent-4", + "sourceFile": "fixtures/code-graph/routes/python/fastapi_models.py", + "targetFile": "fixtures/code-graph/routes/python/fastapi_app.py", + "reason": "The modules instantiate separate FastAPI apps. The models app does not include the other app or its router." + }, + { + "id": "route-containment-independent-5", + "sourceFile": "fixtures/code-graph/routes/python/flask_factory.py", + "targetFile": "fixtures/code-graph/routes/python/flask_app.py", + "reason": "The factory registers its own root and nested blueprints. flask_app.py creates a separate app and blueprint; no cross-module registration exists." + }, + { + "id": "route-containment-independent-6", + "sourceFile": "fixtures/code-graph/routes/typescript/owner-mismatch.ts", + "targetFile": "fixtures/code-graph/routes/typescript/express.ts", + "reason": "Each module creates its own Express app; neither imports or mounts the other. A route with an unresolved controller is not a parent router." + }, + { + "id": "route-containment-independent-7", + "sourceFile": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts", + "targetFile": "fixtures/code-graph/routes/typescript/vue-router.ts", + "reason": "Both modules independently call createRouter. Neither route array declares the other route as a child or imports the other router." + }, + { + "id": "route-containment-independent-8", + "sourceFile": "fixtures/code-graph/routes/typescript/vue-router.ts", + "targetFile": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts", + "reason": "Both modules independently call createRouter. Neither route array declares the other route as a child or imports the other router." + } + ] } diff --git a/tests/qualification/code-graph-v1-topology.json b/tests/qualification/code-graph-v1-topology.json index 81174513e..fd6efb4a8 100644 --- a/tests/qualification/code-graph-v1-topology.json +++ b/tests/qualification/code-graph-v1-topology.json @@ -2,37 +2,37 @@ "schema": "compass.code-graph-topology-policy/1", "topology": { "minimums": { - "communities": 225, - "edges": 1284, - "exactCrossCommunityEdges": 5, - "exactCrossFileEdges": 81, - "exactCrossFileEdgesPerThousandNodes": 63, - "exactEdgeBearingNodePermille": 711, - "exactEdgeBearingNodes": 908, - "exactEdges": 965, + "communities": 232, + "edges": 1265, + "exactCrossCommunityEdges": 3, + "exactCrossFileEdges": 62, + "exactCrossFileEdgesPerThousandNodes": 48, + "exactEdgeBearingNodePermille": 709, + "exactEdgeBearingNodes": 905, + "exactEdges": 946, "exactLargestComponentNodes": 54, - "exactUniqueTypedEndpointPairs": 956, - "exactUniqueTypedEndpointPairsPerThousandNodes": 749, + "exactUniqueTypedEndpointPairs": 938, + "exactUniqueTypedEndpointPairsPerThousandNodes": 735, "nodes": 1276, - "uniqueTypedEndpointPairs": 1261 + "uniqueTypedEndpointPairs": 1243 }, "maximums": { - "communities": 226, - "connectedComponents": 222, - "exactConnectedComponents": 496, - "exactIsolatedNodes": 368, + "communities": 233, + "connectedComponents": 230, + "exactConnectedComponents": 504, + "exactIsolatedNodes": 371, "exactSelfLoops": 0, - "isolatedNodes": 96, + "isolatedNodes": 99, "selfLoops": 0, - "singletonCommunities": 96 + "singletonCommunities": 99 }, "relationshipMinimums": { "calls": { "exactUniqueEndpointPairs": 13 }, "contains": { - "exactCrossFileEdges": 31, - "exactUniqueEndpointPairs": 624 + "exactCrossFileEdges": 12, + "exactUniqueEndpointPairs": 606 }, "documents": { "exactCrossFileEdges": 3, From c20db15b9748b17753d2ff288e73a729c53a4465 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 20:17:17 -0700 Subject: [PATCH 29/97] fix: derive file-route parents from framework semantics --- CHANGELOG.md | 6 + COMPATIBILITY.md | 13 +- MIGRATION.md | 8 +- ...antic_route_parent_development_review.json | 818 ++++++ .../semantic_route_parent_fixture_review.json | 2185 +++++++++++++++++ .../compass-languages/src/frameworks/mod.rs | 2 +- .../compass-resolve/src/frameworks/routes.rs | 173 +- .../src/frameworks/routes/hierarchy.rs | 225 ++ .../compass-resolve/tests/framework_routes.rs | 304 +++ docs/design/code-graph-v1-qualification.md | 15 +- ...ode-graph-intelligence-audit-2026-09-26.md | 88 +- docs/reference/react-framework-graph.md | 27 + .../frontend-react/src/app/admin/layout.tsx | 3 + .../frontend-react/src/app/layout.tsx | 3 + scripts/qualify_react_frontend_graph.py | 20 +- scripts/qualify_react_frontend_graph.sh | 1 + scripts/react_frontend_source_oracle.mjs | 41 +- scripts/react_route_hierarchy_oracle.mjs | 79 + .../react_route_hierarchy_oracle.test.mjs | 43 + .../test_react_frontend_qualification.py | 26 +- .../qualification/code-graph-v1-semantic.json | 60 + .../qualification/code-graph-v1-topology.json | 44 +- .../react-frontend-expectation-policy.json | 15 +- 23 files changed, 3976 insertions(+), 223 deletions(-) create mode 100644 benchmarks/agent_query/semantic_route_parent_development_review.json create mode 100644 benchmarks/agent_query/semantic_route_parent_fixture_review.json create mode 100644 crates/compass-resolve/src/frameworks/routes/hierarchy.rs create mode 100644 fixtures/code-graph/frontend-react/src/app/admin/layout.tsx create mode 100644 fixtures/code-graph/frontend-react/src/app/layout.tsx create mode 100644 scripts/react_route_hierarchy_oracle.mjs create mode 100644 scripts/tests/react_route_hierarchy_oracle.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 623a4d30c..2027e8fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Resolve file-route parents from framework nesting conventions instead of + choosing a nearby module. Preserve layout, flat-route, index, pathless, and + non-nesting distinctions; retain ambiguous parents without inventing edges. + Pages Router, Astro, and HTTP endpoints no longer acquire automatic layout + parents from directory order. Rebuild graphs under framework semantics 8. + - Restrict filesystem route hierarchy to recognized file-route conventions. Independent programmatic routers no longer acquire containment edges merely from shared receiver names and source directories, which could inflate hubs diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 74471833a..352e92b54 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -225,11 +225,22 @@ Framework route hierarchy now requires a recognized filesystem-convention fact from its owning framework producer. A receiver name such as `r` or `app` does not establish parentage between programmatic routes in separate source files. Framework composition rules still own programmatic mounts and groups. The -framework-pack semantics identity advances from 6 to 7, and build-state seals +framework-pack semantics identity initially advanced from 6 to 7, and build-state seals now include that identity. Rebuild existing graphs to remove unsupported containment edges and recompute affected paths, degrees, and communities. Graph/evidence schema majors and immutable historical realizations are unchanged. +Framework semantics 8 additionally replaces first-file directory selection with +framework-specific nesting. Next App Router parents must be layout modules; +flat route parents use filename segments, including pathless and non-nesting +markers; Nuxt parents require a matching page module above a child directory. +Index pages and sibling modules cannot become parents merely through ordering. +Ambiguous nearest parents remain unresolved instead of falling back outward. +Rebuild graphs to correct containment, degrees, paths, and communities. These +rules do not infer custom router configuration, runtime mounts, or an omitted +layout declaration. See the supported boundaries in the +[framework graph reference](docs/reference/react-framework-graph.md). + ### Agent Query View Compass adds the additive strict projection `compass.query.agent-view/1` for diff --git a/MIGRATION.md b/MIGRATION.md index 38a42dc48..90fb5e6eb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -8,11 +8,17 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution Rebuild graphs with programmatic framework routes to remove filesystem-derived -containment between independent routers. Framework-pack semantics version 7 +containment between independent routers and unsupported file-route parents. +Framework-pack semantics version 8 invalidates prior build profiles and build-state seals; disposable framework facts can be rebuilt from source. Hub rankings, navigation paths, and communities can change. Existing historical realizations remain unchanged. +Version 8 corrects the directory-order parent rule introduced before version 7. +File-route siblings, standalone endpoints, and page files without a layout +parent may lose containment edges. Source-proven nested layouts and flat-route +parents can gain the correct edges. The graph schema is unchanged. + Rebuild existing Go graphs to receive the control-initializer and receiver shadowing corrections. Normal builds automatically discard AST cache versions older than 4. Source-proven calls can appear and incorrectly attributed calls diff --git a/benchmarks/agent_query/semantic_route_parent_development_review.json b/benchmarks/agent_query/semantic_route_parent_development_review.json new file mode 100644 index 000000000..c822d509e --- /dev/null +++ b/benchmarks/agent_query/semantic_route_parent_development_review.json @@ -0,0 +1,818 @@ +{ + "schema": "compass.semantic-route-parent-development-review/1", + "scope": "Repeated five-language development comparison and independently registered source-selected Next diagnostic. Neither is fresh held-out evidence or proof of general superiority.", + "runId": "semantic-route-parent-panel-a-01", + "suiteDigest": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "runnerDigest": "173242988699eb6fabd175c293634d3cf37c1394f76429540fd7af2a48ef7026", + "tools": [ + { + "binary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/semantic-route-parent-provenance/compass", + "binarySha256": "9efe548537b106c69d1da7a5ac7955e764e43103e0d1e4a754a3c1880c292bea", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30" + }, + { + "binary": "/Users/haipingfu/.local/bin/graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67" + } + ], + "metrics": { + "compass": { + "text": { + "medianAnswerTokens": 148.0, + "medianFirstPageTokens": 148.0, + "medianWallMs": 462.0, + "passRate": 0.8364, + "passed": 46, + "questions": 55 + }, + "selectedSourceEdges": { + "relationshipPassed": 16, + "allOccurrencesPassed": 16, + "total": 21 + }, + "sourcePaths": { + "passed": 6, + "total": 10 + } + }, + "graphify": { + "text": { + "medianAnswerTokens": 104.0, + "medianFirstPageTokens": 75.0, + "medianWallMs": 166.0, + "passRate": 0.8364, + "passed": 46, + "questions": 55 + }, + "selectedSourceEdges": { + "relationshipPassed": 20, + "allOccurrencesPassed": 17, + "total": 21 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + } + } + }, + "panelProductionDelta": { + "chi": { + "removedNodeIds": 0, + "addedNodeIds": 0, + "changedNodes": 0, + "removedEdgeIds": 0, + "addedEdgeIds": 0, + "changedEdges": 0, + "beforeGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "afterGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5" + }, + "click": { + "removedNodeIds": 0, + "addedNodeIds": 0, + "changedNodes": 0, + "removedEdgeIds": 0, + "addedEdgeIds": 0, + "changedEdges": 0, + "beforeGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "afterGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93" + }, + "jsoup": { + "removedNodeIds": 0, + "addedNodeIds": 0, + "changedNodes": 0, + "removedEdgeIds": 0, + "addedEdgeIds": 0, + "changedEdges": 0, + "beforeGraphSha256": "8035487618e4e96af8b7cc668d92eaea9ee3ec2a831c486d20055bc808fa23f6", + "afterGraphSha256": "8035487618e4e96af8b7cc668d92eaea9ee3ec2a831c486d20055bc808fa23f6" + }, + "redux": { + "removedNodeIds": 0, + "addedNodeIds": 0, + "changedNodes": 0, + "removedEdgeIds": 0, + "addedEdgeIds": 0, + "changedEdges": 0, + "beforeGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "afterGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b" + }, + "walkdir": { + "removedNodeIds": 0, + "addedNodeIds": 0, + "changedNodes": 0, + "removedEdgeIds": 0, + "addedEdgeIds": 0, + "changedEdges": 0, + "beforeGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "afterGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177" + } + }, + "performanceLimitation": "One shared-machine run with overlapping verification work. Latencies are descriptive, not a comparative performance win. Graphify hash attests launcher only, not its full environment.", + "shadcnDiagnostic": { + "commit": "a87a63b2ca25143d26c8bd0903e4e9bc77b3f824", + "sourceScope": "apps/v4/app plus package and TypeScript configuration; source-selected development diagnostic, not held out, not full repository qualification", + "files": 273, + "bytes": 1398028, + "registration": "Expected pairs recorded before inspecting either binary graph for this projection.", + "results": { + "before": { + "graphSha256": "06c4633cc9d6dd31147ee5930920f632e6249500171301b5e02dbfb647c3bc9f", + "nodes": 14762, + "edges": 21316, + "checks": [ + { + "sourceFile": "apps/v4/app/layout.tsx", + "targetFile": "apps/v4/app/(app)/layout.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:279ad634fee3cd57de2942c9a849667d3335ae139ce50078c65a8fd86f4f4e61" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/layout.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": false + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/page.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:0f9e6cff50b2bc845dbf05d3b8645bd1aa63778f267769d5487f0a16e2150cd7" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/[...categories]/page.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:6d0244dae1b62062f05ff67b9f23a439533feaa9c571de7889e200a59ca53997" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/page.tsx", + "targetFile": "apps/v4/app/(app)/blocks/[...categories]/page.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/charts/layout.tsx", + "targetFile": "apps/v4/app/(app)/colors/layout.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/examples/dashboard/page.tsx", + "targetFile": "apps/v4/app/(app)/examples/tasks/page.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + } + ], + "passed": 6, + "total": 7 + }, + "after": { + "graphSha256": "bf891990821aaef52ac8512dcc04d9f5fc7762a744d987823eb6c10ba77d657f", + "nodes": 14762, + "edges": 21307, + "checks": [ + { + "sourceFile": "apps/v4/app/layout.tsx", + "targetFile": "apps/v4/app/(app)/layout.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:279ad634fee3cd57de2942c9a849667d3335ae139ce50078c65a8fd86f4f4e61" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/layout.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:2e8c788629fad70689771c5516055d5eabf914f548cd510774a854616a7319fd" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/page.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:0f9e6cff50b2bc845dbf05d3b8645bd1aa63778f267769d5487f0a16e2150cd7" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/layout.tsx", + "targetFile": "apps/v4/app/(app)/blocks/[...categories]/page.tsx", + "expected": true, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [ + "sha256:6d0244dae1b62062f05ff67b9f23a439533feaa9c571de7889e200a59ca53997" + ], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/blocks/page.tsx", + "targetFile": "apps/v4/app/(app)/blocks/[...categories]/page.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/charts/layout.tsx", + "targetFile": "apps/v4/app/(app)/colors/layout.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + }, + { + "sourceFile": "apps/v4/app/(app)/examples/dashboard/page.tsx", + "targetFile": "apps/v4/app/(app)/examples/tasks/page.tsx", + "expected": false, + "sourceRouteCount": 1, + "targetRouteCount": 1, + "edgeIds": [], + "passed": true + } + ], + "passed": 7, + "total": 7 + } + }, + "removed": [ + { + "id": "sha256:15176e79a2d4b491baab1723dbd53c4ce846a8aa4ac15a1c6498480a32296b6b", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(create)/init/route.ts", + "startByte": 0, + "endByte": 1920, + "startLine": 1, + "startColumn": 0, + "endLine": 67, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:268dbca6ec1676bfe144c88b8dc21c16510716f2fcc274559749ba6fcd9c2cb0", + "kind": "contains", + "source": { + "file": "apps/v4/app/layout.tsx", + "startByte": 0, + "endByte": 3907, + "startLine": 1, + "startColumn": 0, + "endLine": 128, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/api/search/route.ts", + "startByte": 0, + "endByte": 151, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:35e4b09f5e9d7ec5789ae5cc7e20074337bdd731b8cf8d4302783b602a8140ba", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/blocks/page.tsx", + "startByte": 0, + "endByte": 1068, + "startLine": 1, + "startColumn": 0, + "endLine": 43, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/blocks/layout.tsx", + "startByte": 0, + "endByte": 2078, + "startLine": 1, + "startColumn": 0, + "endLine": 80, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:4c7d7b2ea643ea4294ae4b8f0fbfd786abc96f003ea0e8156db150b8f4eb1275", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/(create)/init/route.ts", + "startByte": 0, + "endByte": 1920, + "startLine": 1, + "startColumn": 0, + "endLine": 67, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(create)/init/md/route.ts", + "startByte": 0, + "endByte": 882, + "startLine": 1, + "startColumn": 0, + "endLine": 30, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:4e519c58b5603bb494122ed2fe3b94499c4d133321aeed69e7caea404268ec7a", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/colors/page.tsx", + "startByte": 0, + "endByte": 446, + "startLine": 1, + "startColumn": 0, + "endLine": 18, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/colors/layout.tsx", + "startByte": 0, + "endByte": 2112, + "startLine": 1, + "startColumn": 0, + "endLine": 79, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:5693c412d16e91053c21598d7ec82b0685629700ab0267469d7235a944327b6b", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/(create)/create/loading.tsx", + "startByte": 0, + "endByte": 154, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(create)/create/layout.tsx", + "startByte": 0, + "endByte": 754, + "startLine": 1, + "startColumn": 0, + "endLine": 23, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:5ceffb5d4722f1333b88493a33a5f50faa10a90615a70c109438902c08d65c1a", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/llm/[[...slug]]/route.ts", + "startByte": 0, + "endByte": 1421, + "startLine": 1, + "startColumn": 0, + "endLine": 58, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:b399ea85d01d285ab598d99d49c44080f494e6c65b37681b966dd5afa737b9ef", + "kind": "contains", + "source": { + "file": "apps/v4/app/layout.tsx", + "startByte": 0, + "endByte": 3907, + "startLine": 1, + "startColumn": 0, + "endLine": 128, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/r/registries.json/route.ts", + "startByte": 0, + "endByte": 1818, + "startLine": 1, + "startColumn": 0, + "endLine": 66, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:b41f3e9218ffbab15d46d74f4ec347bd8bf0db083d65e6657c47c09118161bc2", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/(create)/init/route.ts", + "startByte": 0, + "endByte": 1920, + "startLine": 1, + "startColumn": 0, + "endLine": 67, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(create)/init/v0/route.ts", + "startByte": 0, + "endByte": 1285, + "startLine": 1, + "startColumn": 0, + "endLine": 45, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:ca60ee6b934f3f1cbac3cf8123284b7a119f85c01278344b63177bd2b2eb446c", + "kind": "contains", + "source": { + "file": "apps/v4/app/layout.tsx", + "startByte": 0, + "endByte": 3907, + "startLine": 1, + "startColumn": 0, + "endLine": 128, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/typeset.css/route.ts", + "startByte": 0, + "endByte": 543, + "startLine": 1, + "startColumn": 0, + "endLine": 21, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:e8cabf166d7d580a77ce85134330fc65819de4765366f436f6604ebdc4945492", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/(typeset)/typeset/loading.tsx", + "startByte": 0, + "endByte": 159, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(typeset)/typeset/layout.tsx", + "startByte": 0, + "endByte": 799, + "startLine": 1, + "startColumn": 0, + "endLine": 24, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:e9bb286bcda82d0c9b2e0e29237169ca36ca35e5e1ad033b89198f57f9ae2944", + "kind": "contains", + "source": { + "file": "apps/v4/app/layout.tsx", + "startByte": 0, + "endByte": 3907, + "startLine": 1, + "startColumn": 0, + "endLine": 128, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/og/route.tsx", + "startByte": 0, + "endByte": 3402, + "startLine": 1, + "startColumn": 0, + "endLine": 118, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:ff7a1144ac8915185c676676c772e66d08ee6687bc852eab3ed8472e043b11ce", + "kind": "contains", + "source": { + "file": "apps/v4/app/layout.tsx", + "startByte": 0, + "endByte": 3907, + "startLine": 1, + "startColumn": 0, + "endLine": 128, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/rss.xml/route.ts", + "startByte": 0, + "endByte": 1303, + "startLine": 1, + "startColumn": 0, + "endLine": 45, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + } + ], + "added": [ + { + "id": "sha256:2e8c788629fad70689771c5516055d5eabf914f548cd510774a854616a7319fd", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/blocks/layout.tsx", + "startByte": 0, + "endByte": 2078, + "startLine": 1, + "startColumn": 0, + "endLine": 80, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:93c266645b8ffd077f7c13d0cf52d3bc80e3b65070ec5676b0409f3259cb0974", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(create)/create/layout.tsx", + "startByte": 0, + "endByte": 754, + "startLine": 1, + "startColumn": 0, + "endLine": 23, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:bda10b102f9b54e0128eba4ec16dc4f1912aac605e9c300858d370cad5d643d7", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/colors/layout.tsx", + "startByte": 0, + "endByte": 2112, + "startLine": 1, + "startColumn": 0, + "endLine": 79, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "id": "sha256:c5297dee9df67aa4cba652d6c21327317420c5ca218ec7f6657434b81234587f", + "kind": "contains", + "source": { + "file": "apps/v4/app/(app)/layout.tsx", + "startByte": 0, + "endByte": 526, + "startLine": 1, + "startColumn": 0, + "endLine": 16, + "endColumn": 0 + }, + "target": { + "file": "apps/v4/app/(app)/(typeset)/typeset/layout.tsx", + "startByte": 0, + "endByte": 799, + "startLine": 1, + "startColumn": 0, + "endLine": 24, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + } + ], + "sourceHashes": { + "apps/v4/app/(app)/(create)/create/layout.tsx": { + "bytes": 754, + "sha256": "199daabcf33d69c31ee72b1d29ea9341b971df760dbfe0b49b6b670508e1ed6b", + "gitBlob": "51bc22a1d22570ca20b51fd14aac1e83a5aaa55f" + }, + "apps/v4/app/(app)/(create)/create/loading.tsx": { + "bytes": 154, + "sha256": "c5959e1b8fb3c433111f2fe360760586e9bb3bfcbb424813570eae46d3a060ec", + "gitBlob": "0922185cff0aa5e5f554cca5d68d00717ba2d64c" + }, + "apps/v4/app/(app)/(create)/init/md/route.ts": { + "bytes": 882, + "sha256": "5cbc9cdf08f08c092eb0e3bae9e645f0b08b45abae42d51edfe03e3c7d9494d8", + "gitBlob": "1b4f1129a88853ed31d6df774a56a378c9b7ceed" + }, + "apps/v4/app/(app)/(create)/init/route.ts": { + "bytes": 1920, + "sha256": "0838621b8b2bbe90f0534790aa8259232d4d5792798ceea8e228b78322c58a20", + "gitBlob": "dc8eae9cbd63720fe461efe11cbe866123ceab7c" + }, + "apps/v4/app/(app)/(create)/init/v0/route.ts": { + "bytes": 1285, + "sha256": "c61ede79717f2f1b49b12b72671d37f28cc651a346337268f7dc8c9819726951", + "gitBlob": "a8b0bc61fb0db58fddded97b9deecae57260dfb0" + }, + "apps/v4/app/(app)/(typeset)/typeset/layout.tsx": { + "bytes": 799, + "sha256": "f09ab4e53dcb36d40b4f7a12019ae136146344863ac0f61a098829ecf32b8b08", + "gitBlob": "dbd826a44a05f7151298050fc211d3c69f6fb999" + }, + "apps/v4/app/(app)/(typeset)/typeset/loading.tsx": { + "bytes": 159, + "sha256": "0382fac46d0c64a892227d2399d6b3e6db2dc947914931452298e19d939da49b", + "gitBlob": "8b50e3e79bf209fbb0e47d67f3a503a1e48e29e2" + }, + "apps/v4/app/(app)/blocks/layout.tsx": { + "bytes": 2078, + "sha256": "6d4f4dca39fa0e5047d3bfae5783627f590e356982e40f983d100c3260a3d5da", + "gitBlob": "70a9e67d546496d7799b0064106ec03bbf87f975" + }, + "apps/v4/app/(app)/blocks/page.tsx": { + "bytes": 1068, + "sha256": "248322276f09adb17ab7accfc2a869dcebd267d468c34dcca877b54d0597ccd1", + "gitBlob": "2fff179f0d318ea7b784f7dd4d338d41c111c6f9" + }, + "apps/v4/app/(app)/colors/layout.tsx": { + "bytes": 2112, + "sha256": "32f230bd2572945b0a59b0df7ffeeda18ffe06be007e2c42fd2c6871fc19de7f", + "gitBlob": "4813f342a265048877497a90664156667c5f813e" + }, + "apps/v4/app/(app)/colors/page.tsx": { + "bytes": 446, + "sha256": "55d52c1591df7e2fcd8504afd2f13e5d884209309bd7f463d78675078f624219", + "gitBlob": "be71657930192672d0342ebe3a04f2e3a7ec58cf" + }, + "apps/v4/app/(app)/layout.tsx": { + "bytes": 526, + "sha256": "8efe40cc4f24659566fd9b47cd93d09d058d637b544725057b7e60c5f7291453", + "gitBlob": "ca5f8e14502fe7568cef8bc79925789b51ffdf7f" + }, + "apps/v4/app/(app)/llm/[[...slug]]/route.ts": { + "bytes": 1421, + "sha256": "fef8ef73e8c6a97eceace445fc89683b4f69ed094a526e655f06b404afe952bb", + "gitBlob": "baa0eafa4fba2a32a2a98fbc123ffa427ce42cea" + }, + "apps/v4/app/api/search/route.ts": { + "bytes": 151, + "sha256": "64ea20fbd34200da023c4dd88c204650509a12dcfcc3b592fa33e8d1e1f85aea", + "gitBlob": "3bafe9c5a2e3970d99fc097b373159c7ff13259b" + }, + "apps/v4/app/layout.tsx": { + "bytes": 3907, + "sha256": "ae953cf25882c0cee17725a9da68a5a6fe255f6adc3d7629bbc39cf92063d3ab", + "gitBlob": "ade2f148715e346b18c844e010ae02fd9f15e624" + }, + "apps/v4/app/og/route.tsx": { + "bytes": 3402, + "sha256": "8b69aa880c787f6d80055edbc8f612dc63fbeabbb971f94e8d5c979072192af2", + "gitBlob": "1710b3212c7e90541a5201cd3c7918bfdc65c4aa" + }, + "apps/v4/app/r/registries.json/route.ts": { + "bytes": 1818, + "sha256": "3ef3d5b3391e3f67d1c6ed6037707b98869b80b4f52c20ee4606a72c32b05cf1", + "gitBlob": "a2f91a2dfc77129c4f895f319fdefd6a1668beda" + }, + "apps/v4/app/rss.xml/route.ts": { + "bytes": 1303, + "sha256": "9a565f05e2345d4428074dfd21741301ed882f6fe67875fde5a6f308868448a8", + "gitBlob": "a83241a23baf8ffaed4854b3fa29f972a8885dfc" + }, + "apps/v4/app/typeset.css/route.ts": { + "bytes": 543, + "sha256": "c8ce1feb7e038fb598d9b8081d734b1c7d1e08d138d0d2a887bc1cba865c9dc6", + "gitBlob": "e65708d44aa78c68dc568c1a325506a1ed47a3d0" + } + }, + "sourceReview": "All 13 removed links inspected: 9 target HTTP route handlers, 2 page-to-layout reversals, 2 loading-to-layout reversals. Four added links connect AppLayout to nested layout modules which render children. This is an audit of this changed edge set, not a graph-wide precision estimate.", + "unchangedNodesExceptCommunity": true, + "unchangedCommonEdges": true, + "genericDeltaHelperLimitation": "The raw delta also records failures from an inapplicable fixture-only negative checker; those are not scored for this real source projection. The registered witness-results file is the diagnostic oracle.", + "reference": "https://nextjs.org/docs/app/getting-started/layouts-and-pages" + }, + "artifactSha256": { + "runs/semantic-route-parent-panel-a-01/run.json": "fd990ebabbc950c1f4678e390e4b4d53425d7370cfb95e919db8735dc6fd4f07", + "semantic-route-parent-panel-a-path-audit-01.json": "7e00690db7fe2f929162d574db08c3097ed40cafb7b6fb789ec6d06416767125", + "semantic-route-parent-shadcn-diagnostic/source-review.json": "c2ae84bd110910638d38fda0d6d1078e157d9e529f7b317c62d9adb138004b19", + "semantic-route-parent-shadcn-diagnostic/witness-results.json": "3ded522ca70836326910d21f78897bba5ce5258578d458efa9b26b7bfa62c580", + "semantic-route-parent-shadcn-diagnostic/delta.json": "402428f15ee4da3bd21eca1e290c13659c9f0858899734c9e38010b79bc41c6d", + "semantic-route-parent-provenance/manifest.json": "06906ec5a324add01dee7fef9194972cab83e40d38a28cc18a869d3339da7789", + "semantic-route-parent-panel-a-chi-audit-01.json": "97260bd7333630dd078b069ab90347476c8916a564ba617168912dcae377e66f", + "semantic-route-parent-panel-a-click-audit-01.json": "44469c4b7f85d4d2f63334438e2f87bf877704922b6eedbdfb9db82d6b5252bd", + "semantic-route-parent-panel-a-jsoup-audit-01.json": "64bfb2053434959c62f64bfc9158d532441c204ff01289744e13091f4b18be22", + "semantic-route-parent-panel-a-redux-audit-01.json": "3245cb05a41d8de5d9eb23f98b7c96967ccd833f319be0249866027e27f2dd43", + "semantic-route-parent-panel-a-walkdir-audit-01.json": "08ce5010bc936dfcb5f5d76661bfefcfb7b1fb194b08714eecf802b26f55e3a7", + "semantic-route-parent-native-03.log": "1c2891fbb20b755cbf539a709ba201cb88eaef5095baba34350dbd124a30466c", + "semantic-route-parent-clippy-01.log": "23c46dfc9f6c9cbb620ba34e0db71c19bbb4abe80b26d20cffa56468b69caa07", + "semantic-route-parent-clippy-02.log": "ca46c5728557323cbc93705205a9f88d6bb98583b578e2b494ee01d768492a6f", + "semantic-route-parent-clippy-03.log": "fff5f30464d4f3ef197fa2e8ab93c1a1304fda11a03bfb079432b2a0c4a79218", + "semantic-route-parent-build-01.log": "6700369d26cb823392e539ac8a47c4d937fdf30db8c53bfac640d03ad3b282a7", + "semantic-route-parent-python-05.log": "f62958151715e26c01500d1393ad0e69837f6579714e0f99de70ef376313ce3d", + "semantic-route-parent-oracle-03.log": "42ca74749b922ec801cb27b67a4d6fd8576478696cf8271c44dc2d297fe7d5a4", + "semantic-route-parent-benchmark-tests-01.log": "c8bd418cb4120ed7a029b186d3ff1501dd56ff23ac1a065dfd27082b44903e4a", + "semantic-route-parent-boundary-01.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "semantic-route-parent-frontend-debug/result-02.json": "5333e65df4f3282f715aeb1e9dd0ff21c8013497e20e988eb84f7a5131f75c68", + "semantic-route-parent-qualification-01.log": "c7d420bc8df5baecb5f9e0898d9833621c8cd80e05493221a157c39f2ea0b54b" + }, + "verification": { + "native": { + "passed": 1436, + "failed": 0, + "ignored": 2, + "limitation": "Before final needless-borrow correction; final corrected production built and passed Clippy." + }, + "scriptTests": 88, + "sourceOracleCases": 15, + "benchmarkTests": 97, + "frontendDebugCheck": "Passed source-oracle and explicit layout-pair checks on positive/negative fixtures; no repeat-build or release equivalence claimed by this standalone check.", + "fullQualification": "First attempt stopped at missing default parser bundle. Second attempt is live session 64446 with explicit existing per-worktree bundle; log semantic-route-parent-qualification-02.log. No full pass claimed.", + "pinnedHierarchy": "Three historical hierarchy scorecards invalidated; independent source re-review remains required." + } +} diff --git a/benchmarks/agent_query/semantic_route_parent_fixture_review.json b/benchmarks/agent_query/semantic_route_parent_fixture_review.json new file mode 100644 index 000000000..897f0e5dd --- /dev/null +++ b/benchmarks/agent_query/semantic_route_parent_fixture_review.json @@ -0,0 +1,2185 @@ +{ + "schema": "compass.semantic-route-parent-fixture-review/1", + "scope": "Development fixture review, not representative real-repository precision or Graphify superiority", + "beforeBinarySha256": "872aff05f6c1723d27ee96230a4e315d3f0d6e1849387de09530b907f0ff3f40", + "afterBinarySha256": "9efe548537b106c69d1da7a5ac7955e764e43103e0d1e4a754a3c1880c292bea", + "original": { + "beforeGraphSha256": "996ae5b52508a6b8b099624c0bb52163242d10e9a440a6d3a3a7203d3d2900d3", + "afterGraphSha256": "ae449b966b660b2f714a155e18075cb408246f4a66abe094999395abaaad4438", + "beforeMetrics": { + "communities": 232, + "connectedComponents": 229, + "crossCommunityEdges": 3, + "crossFileEdges": 63, + "crossFileEdgesPerThousandNodes": 49, + "edgeBearingNodes": 1178, + "edgeBearingNodePermille": 923, + "edges": 1270, + "exactConnectedComponents": 503, + "exactCrossCommunityEdges": 3, + "exactCrossFileEdges": 62, + "exactCrossFileEdgesPerThousandNodes": 48, + "exactEdgeBearingNodes": 906, + "exactEdgeBearingNodePermille": 710, + "exactEdges": 951, + "exactIsolatedNodes": 370, + "exactLargestComponentNodes": 54, + "exactSelfLoops": 0, + "exactUniqueTypedEndpointPairs": 943, + "exactUniqueTypedEndpointPairsPerThousandNodes": 739, + "isolatedNodes": 98, + "largestComponentNodes": 81, + "nodes": 1276, + "selfLoops": 0, + "singletonCommunities": 98, + "uniqueTypedEndpointPairs": 1248, + "uniqueTypedEndpointPairsPerThousandNodes": 978, + "byRelation": { + "aliases": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 14, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "calls": { + "crossFileEdges": 1, + "edgeBearingNodes": 132, + "edges": 103, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 24, + "exactEdges": 17, + "exactUniqueEndpointPairs": 13, + "uniqueEndpointPairs": 88 + }, + "consumes": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "contains": { + "crossFileEdges": 12, + "edgeBearingNodes": 757, + "edges": 606, + "exactCrossFileEdges": 12, + "exactEdgeBearingNodes": 757, + "exactEdges": 606, + "exactUniqueEndpointPairs": 606, + "uniqueEndpointPairs": 606 + }, + "decorates": { + "crossFileEdges": 0, + "edgeBearingNodes": 36, + "edges": 18, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 18 + }, + "depends_on": { + "crossFileEdges": 0, + "edgeBearingNodes": 20, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 20, + "exactEdges": 16, + "exactUniqueEndpointPairs": 16, + "uniqueEndpointPairs": 16 + }, + "documents": { + "crossFileEdges": 3, + "edgeBearingNodes": 5, + "edges": 3, + "exactCrossFileEdges": 3, + "exactEdgeBearingNodes": 5, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "exports": { + "crossFileEdges": 0, + "edgeBearingNodes": 49, + "edges": 27, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 48, + "exactEdges": 26, + "exactUniqueEndpointPairs": 26, + "uniqueEndpointPairs": 27 + }, + "extends": { + "crossFileEdges": 0, + "edgeBearingNodes": 28, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 11, + "exactEdges": 6, + "exactUniqueEndpointPairs": 6, + "uniqueEndpointPairs": 16 + }, + "handles": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "implements": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "imports": { + "crossFileEdges": 12, + "edgeBearingNodes": 210, + "edges": 144, + "exactCrossFileEdges": 12, + "exactEdgeBearingNodes": 50, + "exactEdges": 29, + "exactUniqueEndpointPairs": 29, + "uniqueEndpointPairs": 144 + }, + "instantiates": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 6 + }, + "maps_to": { + "crossFileEdges": 10, + "edgeBearingNodes": 19, + "edges": 10, + "exactCrossFileEdges": 10, + "exactEdgeBearingNodes": 19, + "exactEdges": 10, + "exactUniqueEndpointPairs": 10, + "uniqueEndpointPairs": 10 + }, + "mixes_in": { + "crossFileEdges": 0, + "edgeBearingNodes": 4, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + }, + "overrides": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "produces": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "publishes": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "reads": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "references": { + "crossFileEdges": 7, + "edgeBearingNodes": 200, + "edges": 137, + "exactCrossFileEdges": 7, + "exactEdgeBearingNodes": 80, + "exactEdges": 52, + "exactUniqueEndpointPairs": 49, + "uniqueEndpointPairs": 131 + }, + "registers": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "renders": { + "crossFileEdges": 4, + "edgeBearingNodes": 7, + "edges": 5, + "exactCrossFileEdges": 4, + "exactEdgeBearingNodes": 7, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "returns": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 6 + }, + "routes_to": { + "crossFileEdges": 14, + "edgeBearingNodes": 197, + "edges": 114, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 197, + "exactEdges": 114, + "exactUniqueEndpointPairs": 113, + "uniqueEndpointPairs": 113 + }, + "schedules": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "subscribes": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 3, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "tests": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "triggers": { + "crossFileEdges": 0, + "edgeBearingNodes": 13, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 13, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "type_of": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 8, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 9, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 8 + }, + "writes": { + "crossFileEdges": 0, + "edgeBearingNodes": 3, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 3, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + } + } + }, + "afterMetrics": { + "communities": 241, + "connectedComponents": 238, + "crossCommunityEdges": 3, + "crossFileEdges": 51, + "crossFileEdgesPerThousandNodes": 39, + "edgeBearingNodes": 1177, + "edgeBearingNodePermille": 922, + "edges": 1258, + "exactConnectedComponents": 512, + "exactCrossCommunityEdges": 3, + "exactCrossFileEdges": 50, + "exactCrossFileEdgesPerThousandNodes": 39, + "exactEdgeBearingNodes": 905, + "exactEdgeBearingNodePermille": 709, + "exactEdges": 939, + "exactIsolatedNodes": 371, + "exactLargestComponentNodes": 54, + "exactSelfLoops": 0, + "exactUniqueTypedEndpointPairs": 931, + "exactUniqueTypedEndpointPairsPerThousandNodes": 729, + "isolatedNodes": 99, + "largestComponentNodes": 81, + "nodes": 1276, + "selfLoops": 0, + "singletonCommunities": 99, + "uniqueTypedEndpointPairs": 1236, + "uniqueTypedEndpointPairsPerThousandNodes": 968, + "byRelation": { + "aliases": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 14, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "calls": { + "crossFileEdges": 1, + "edgeBearingNodes": 132, + "edges": 103, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 24, + "exactEdges": 17, + "exactUniqueEndpointPairs": 13, + "uniqueEndpointPairs": 88 + }, + "consumes": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "contains": { + "crossFileEdges": 0, + "edgeBearingNodes": 742, + "edges": 594, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 742, + "exactEdges": 594, + "exactUniqueEndpointPairs": 594, + "uniqueEndpointPairs": 594 + }, + "decorates": { + "crossFileEdges": 0, + "edgeBearingNodes": 36, + "edges": 18, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 18 + }, + "depends_on": { + "crossFileEdges": 0, + "edgeBearingNodes": 20, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 20, + "exactEdges": 16, + "exactUniqueEndpointPairs": 16, + "uniqueEndpointPairs": 16 + }, + "documents": { + "crossFileEdges": 3, + "edgeBearingNodes": 5, + "edges": 3, + "exactCrossFileEdges": 3, + "exactEdgeBearingNodes": 5, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "exports": { + "crossFileEdges": 0, + "edgeBearingNodes": 49, + "edges": 27, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 48, + "exactEdges": 26, + "exactUniqueEndpointPairs": 26, + "uniqueEndpointPairs": 27 + }, + "extends": { + "crossFileEdges": 0, + "edgeBearingNodes": 28, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 11, + "exactEdges": 6, + "exactUniqueEndpointPairs": 6, + "uniqueEndpointPairs": 16 + }, + "handles": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "implements": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "imports": { + "crossFileEdges": 12, + "edgeBearingNodes": 210, + "edges": 144, + "exactCrossFileEdges": 12, + "exactEdgeBearingNodes": 50, + "exactEdges": 29, + "exactUniqueEndpointPairs": 29, + "uniqueEndpointPairs": 144 + }, + "instantiates": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 6 + }, + "maps_to": { + "crossFileEdges": 10, + "edgeBearingNodes": 19, + "edges": 10, + "exactCrossFileEdges": 10, + "exactEdgeBearingNodes": 19, + "exactEdges": 10, + "exactUniqueEndpointPairs": 10, + "uniqueEndpointPairs": 10 + }, + "mixes_in": { + "crossFileEdges": 0, + "edgeBearingNodes": 4, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + }, + "overrides": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "produces": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "publishes": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "reads": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "references": { + "crossFileEdges": 7, + "edgeBearingNodes": 200, + "edges": 137, + "exactCrossFileEdges": 7, + "exactEdgeBearingNodes": 80, + "exactEdges": 52, + "exactUniqueEndpointPairs": 49, + "uniqueEndpointPairs": 131 + }, + "registers": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "renders": { + "crossFileEdges": 4, + "edgeBearingNodes": 7, + "edges": 5, + "exactCrossFileEdges": 4, + "exactEdgeBearingNodes": 7, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "returns": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 6 + }, + "routes_to": { + "crossFileEdges": 14, + "edgeBearingNodes": 197, + "edges": 114, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 197, + "exactEdges": 114, + "exactUniqueEndpointPairs": 113, + "uniqueEndpointPairs": 113 + }, + "schedules": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "subscribes": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 3, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "tests": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "triggers": { + "crossFileEdges": 0, + "edgeBearingNodes": 13, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 13, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "type_of": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 8, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 9, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 8 + }, + "writes": { + "crossFileEdges": 0, + "edgeBearingNodes": 3, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 3, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + } + } + }, + "removed": [ + { + "edgeId": "sha256:3aa44343cb0774ab19678aab3ea377d69781a9b0e22e6c7bf4bf7ba54f24f616", + "kind": "contains", + "framework": "react-router", + "source": { + "file": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "startByte": 228, + "endByte": 274, + "startLine": 9, + "startColumn": 2, + "endLine": 9, + "endColumn": 48 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "startByte": 0, + "endByte": 291, + "startLine": 1, + "startColumn": 0, + "endLine": 12, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:3c0731a84fa2ef12843d75ebae20556eda6a3372c1756380c1de41b5b24e5cdb", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/api/items/[id].ts", + "startByte": 0, + "endByte": 143, + "startLine": 1, + "startColumn": 0, + "endLine": 8, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:4e8881635f480ed920670d90fa8d2c3051b9d98b5d059349e87b8d29ad85d84e", + "kind": "contains", + "framework": "next", + "source": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/page.tsx", + "startByte": 0, + "endByte": 123, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/settings/page.tsx", + "startByte": 0, + "endByte": 143, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:5ae11324f64f9a609b5a24016f510b99f4f51ea0a2860061408943c409c8dbc4", + "kind": "contains", + "framework": "nuxt", + "source": { + "file": "fixtures/code-graph/routes/typescript/nuxt/server/api/users.get.ts", + "startByte": 0, + "endByte": 54, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/nuxt/server/api/users/[id].post.ts", + "startByte": 0, + "endByte": 57, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:5e07c1998ceb2f807df7ef57d15c19b0ba1c97364ac7c4cb7b0768e10387557b", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/users/[id].ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:74b5ddc96a4ba0ec2403b0a4ad937f2ba30d36792459bbaa69a607d40a39a775", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/api/items/[id].ts", + "startByte": 0, + "endByte": 143, + "startLine": 1, + "startColumn": 0, + "endLine": 8, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:af33acc5c2410bfe410a84dcd588d63735560f116d81da4ceb1c7c369c5dc79f", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/files/[...rest].astro", + "startByte": 0, + "endByte": 56, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:bb46aca40a48532523fe60b675b4535a3e21c26156e42364bf3b70d4ed6c1c57", + "kind": "contains", + "framework": "sveltekit", + "source": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "startByte": 0, + "endByte": 75, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:be0806a621daff555e8e36c37ba605edfbf50402e8d893ab6360c06feb84182c", + "kind": "contains", + "framework": "react-router", + "source": { + "file": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "startByte": 0, + "endByte": 291, + "startLine": 1, + "startColumn": 0, + "endLine": 12, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "startByte": 228, + "endByte": 274, + "startLine": 9, + "startColumn": 2, + "endLine": 9, + "endColumn": 48 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:d46d3de0405622e796a7a6f0dcc243408431e4164c43d12c8a32847aa3b5b02d", + "kind": "contains", + "framework": "sveltekit", + "source": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "startByte": 0, + "endByte": 75, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:ecfc5aa101e90cb633af15515c78217dce4aa5e4ceb17ab4361dbc70e490bd14", + "kind": "contains", + "framework": "next", + "source": { + "file": "fixtures/code-graph/frontend-react/src/app/page.tsx", + "startByte": 0, + "endByte": 118, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/page.tsx", + "startByte": 0, + "endByte": 123, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:f3ec5a2fafccd62c8b555db443547e3846923fb5101e9a26d70dadc737902266", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/blog/[slug].astro", + "startByte": 0, + "endByte": 55, + "startLine": 1, + "startColumn": 0, + "endLine": 5, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + } + ], + "added": [], + "nodesUnchangedExceptCommunity": true, + "commonEdgesUnchanged": true + }, + "expanded": { + "beforeGraphSha256": "f151e061efa28f476db8cbcded366e647560b3480fa24470bed11d962c351489", + "afterGraphSha256": "35fc2c2df9b7160000c1187a6519f17a89bdea18d3eb247c9c75532ea23c7367", + "beforeMetrics": { + "communities": 233, + "connectedComponents": 230, + "crossCommunityEdges": 3, + "crossFileEdges": 68, + "crossFileEdgesPerThousandNodes": 52, + "edgeBearingNodes": 1194, + "edgeBearingNodePermille": 924, + "edges": 1293, + "exactConnectedComponents": 506, + "exactCrossCommunityEdges": 3, + "exactCrossFileEdges": 67, + "exactCrossFileEdgesPerThousandNodes": 51, + "exactEdgeBearingNodes": 920, + "exactEdgeBearingNodePermille": 712, + "exactEdges": 972, + "exactIsolatedNodes": 372, + "exactLargestComponentNodes": 54, + "exactSelfLoops": 0, + "exactUniqueTypedEndpointPairs": 964, + "exactUniqueTypedEndpointPairsPerThousandNodes": 746, + "isolatedNodes": 98, + "largestComponentNodes": 81, + "nodes": 1292, + "selfLoops": 0, + "singletonCommunities": 98, + "uniqueTypedEndpointPairs": 1271, + "uniqueTypedEndpointPairsPerThousandNodes": 983, + "byRelation": { + "aliases": { + "crossFileEdges": 0, + "edgeBearingNodes": 18, + "edges": 9, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 18, + "exactEdges": 9, + "exactUniqueEndpointPairs": 9, + "uniqueEndpointPairs": 9 + }, + "calls": { + "crossFileEdges": 1, + "edgeBearingNodes": 132, + "edges": 103, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 24, + "exactEdges": 17, + "exactUniqueEndpointPairs": 13, + "uniqueEndpointPairs": 88 + }, + "consumes": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "contains": { + "crossFileEdges": 15, + "edgeBearingNodes": 769, + "edges": 617, + "exactCrossFileEdges": 15, + "exactEdgeBearingNodes": 769, + "exactEdges": 617, + "exactUniqueEndpointPairs": 617, + "uniqueEndpointPairs": 617 + }, + "decorates": { + "crossFileEdges": 0, + "edgeBearingNodes": 36, + "edges": 18, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 18 + }, + "depends_on": { + "crossFileEdges": 0, + "edgeBearingNodes": 20, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 20, + "exactEdges": 16, + "exactUniqueEndpointPairs": 16, + "uniqueEndpointPairs": 16 + }, + "documents": { + "crossFileEdges": 3, + "edgeBearingNodes": 5, + "edges": 3, + "exactCrossFileEdges": 3, + "exactEdgeBearingNodes": 5, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "exports": { + "crossFileEdges": 0, + "edgeBearingNodes": 53, + "edges": 29, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 52, + "exactEdges": 28, + "exactUniqueEndpointPairs": 28, + "uniqueEndpointPairs": 29 + }, + "extends": { + "crossFileEdges": 0, + "edgeBearingNodes": 28, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 11, + "exactEdges": 6, + "exactUniqueEndpointPairs": 6, + "uniqueEndpointPairs": 16 + }, + "handles": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "implements": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "imports": { + "crossFileEdges": 14, + "edgeBearingNodes": 212, + "edges": 146, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 52, + "exactEdges": 31, + "exactUniqueEndpointPairs": 31, + "uniqueEndpointPairs": 146 + }, + "instantiates": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 6 + }, + "maps_to": { + "crossFileEdges": 10, + "edgeBearingNodes": 19, + "edges": 10, + "exactCrossFileEdges": 10, + "exactEdgeBearingNodes": 19, + "exactEdges": 10, + "exactUniqueEndpointPairs": 10, + "uniqueEndpointPairs": 10 + }, + "mixes_in": { + "crossFileEdges": 0, + "edgeBearingNodes": 4, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + }, + "overrides": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "produces": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "publishes": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "reads": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "references": { + "crossFileEdges": 7, + "edgeBearingNodes": 206, + "edges": 141, + "exactCrossFileEdges": 7, + "exactEdgeBearingNodes": 84, + "exactEdges": 54, + "exactUniqueEndpointPairs": 51, + "uniqueEndpointPairs": 135 + }, + "registers": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "renders": { + "crossFileEdges": 4, + "edgeBearingNodes": 7, + "edges": 5, + "exactCrossFileEdges": 4, + "exactEdgeBearingNodes": 7, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "returns": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 6 + }, + "routes_to": { + "crossFileEdges": 14, + "edgeBearingNodes": 201, + "edges": 116, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 201, + "exactEdges": 116, + "exactUniqueEndpointPairs": 115, + "uniqueEndpointPairs": 115 + }, + "schedules": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "subscribes": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 3, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "tests": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "triggers": { + "crossFileEdges": 0, + "edgeBearingNodes": 13, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 13, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "type_of": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 8, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 9, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 8 + }, + "writes": { + "crossFileEdges": 0, + "edgeBearingNodes": 3, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 3, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + } + } + }, + "afterMetrics": { + "communities": 239, + "connectedComponents": 236, + "crossCommunityEdges": 3, + "crossFileEdges": 57, + "crossFileEdgesPerThousandNodes": 44, + "edgeBearingNodes": 1193, + "edgeBearingNodePermille": 923, + "edges": 1282, + "exactConnectedComponents": 512, + "exactCrossCommunityEdges": 3, + "exactCrossFileEdges": 56, + "exactCrossFileEdgesPerThousandNodes": 43, + "exactEdgeBearingNodes": 919, + "exactEdgeBearingNodePermille": 711, + "exactEdges": 961, + "exactIsolatedNodes": 373, + "exactLargestComponentNodes": 54, + "exactSelfLoops": 0, + "exactUniqueTypedEndpointPairs": 953, + "exactUniqueTypedEndpointPairsPerThousandNodes": 737, + "isolatedNodes": 99, + "largestComponentNodes": 81, + "nodes": 1292, + "selfLoops": 0, + "singletonCommunities": 99, + "uniqueTypedEndpointPairs": 1260, + "uniqueTypedEndpointPairsPerThousandNodes": 975, + "byRelation": { + "aliases": { + "crossFileEdges": 0, + "edgeBearingNodes": 18, + "edges": 9, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 18, + "exactEdges": 9, + "exactUniqueEndpointPairs": 9, + "uniqueEndpointPairs": 9 + }, + "calls": { + "crossFileEdges": 1, + "edgeBearingNodes": 132, + "edges": 103, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 24, + "exactEdges": 17, + "exactUniqueEndpointPairs": 13, + "uniqueEndpointPairs": 88 + }, + "consumes": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "contains": { + "crossFileEdges": 4, + "edgeBearingNodes": 757, + "edges": 606, + "exactCrossFileEdges": 4, + "exactEdgeBearingNodes": 757, + "exactEdges": 606, + "exactUniqueEndpointPairs": 606, + "uniqueEndpointPairs": 606 + }, + "decorates": { + "crossFileEdges": 0, + "edgeBearingNodes": 36, + "edges": 18, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 18 + }, + "depends_on": { + "crossFileEdges": 0, + "edgeBearingNodes": 20, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 20, + "exactEdges": 16, + "exactUniqueEndpointPairs": 16, + "uniqueEndpointPairs": 16 + }, + "documents": { + "crossFileEdges": 3, + "edgeBearingNodes": 5, + "edges": 3, + "exactCrossFileEdges": 3, + "exactEdgeBearingNodes": 5, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "exports": { + "crossFileEdges": 0, + "edgeBearingNodes": 53, + "edges": 29, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 52, + "exactEdges": 28, + "exactUniqueEndpointPairs": 28, + "uniqueEndpointPairs": 29 + }, + "extends": { + "crossFileEdges": 0, + "edgeBearingNodes": 28, + "edges": 16, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 11, + "exactEdges": 6, + "exactUniqueEndpointPairs": 6, + "uniqueEndpointPairs": 16 + }, + "handles": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "implements": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "imports": { + "crossFileEdges": 14, + "edgeBearingNodes": 212, + "edges": 146, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 52, + "exactEdges": 31, + "exactUniqueEndpointPairs": 31, + "uniqueEndpointPairs": 146 + }, + "instantiates": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 6 + }, + "maps_to": { + "crossFileEdges": 10, + "edgeBearingNodes": 19, + "edges": 10, + "exactCrossFileEdges": 10, + "exactEdgeBearingNodes": 19, + "exactEdges": 10, + "exactUniqueEndpointPairs": 10, + "uniqueEndpointPairs": 10 + }, + "mixes_in": { + "crossFileEdges": 0, + "edgeBearingNodes": 4, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 4, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + }, + "overrides": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "produces": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "publishes": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "reads": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "references": { + "crossFileEdges": 7, + "edgeBearingNodes": 206, + "edges": 141, + "exactCrossFileEdges": 7, + "exactEdgeBearingNodes": 84, + "exactEdges": 54, + "exactUniqueEndpointPairs": 51, + "uniqueEndpointPairs": 135 + }, + "registers": { + "crossFileEdges": 0, + "edgeBearingNodes": 8, + "edges": 4, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 4 + }, + "renders": { + "crossFileEdges": 4, + "edgeBearingNodes": 7, + "edges": 5, + "exactCrossFileEdges": 4, + "exactEdgeBearingNodes": 7, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "returns": { + "crossFileEdges": 0, + "edgeBearingNodes": 12, + "edges": 6, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 8, + "exactEdges": 4, + "exactUniqueEndpointPairs": 4, + "uniqueEndpointPairs": 6 + }, + "routes_to": { + "crossFileEdges": 14, + "edgeBearingNodes": 201, + "edges": 116, + "exactCrossFileEdges": 14, + "exactEdgeBearingNodes": 201, + "exactEdges": 116, + "exactUniqueEndpointPairs": 115, + "uniqueEndpointPairs": 115 + }, + "schedules": { + "crossFileEdges": 0, + "edgeBearingNodes": 10, + "edges": 5, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 10, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 5 + }, + "subscribes": { + "crossFileEdges": 0, + "edgeBearingNodes": 6, + "edges": 3, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 6, + "exactEdges": 3, + "exactUniqueEndpointPairs": 3, + "uniqueEndpointPairs": 3 + }, + "tests": { + "crossFileEdges": 0, + "edgeBearingNodes": 2, + "edges": 1, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 2, + "exactEdges": 1, + "exactUniqueEndpointPairs": 1, + "uniqueEndpointPairs": 1 + }, + "triggers": { + "crossFileEdges": 0, + "edgeBearingNodes": 13, + "edges": 7, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 13, + "exactEdges": 7, + "exactUniqueEndpointPairs": 7, + "uniqueEndpointPairs": 7 + }, + "type_of": { + "crossFileEdges": 0, + "edgeBearingNodes": 14, + "edges": 8, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 9, + "exactEdges": 5, + "exactUniqueEndpointPairs": 5, + "uniqueEndpointPairs": 8 + }, + "writes": { + "crossFileEdges": 0, + "edgeBearingNodes": 3, + "edges": 2, + "exactCrossFileEdges": 0, + "exactEdgeBearingNodes": 3, + "exactEdges": 2, + "exactUniqueEndpointPairs": 2, + "uniqueEndpointPairs": 2 + } + } + }, + "removed": [ + { + "edgeId": "sha256:3aa44343cb0774ab19678aab3ea377d69781a9b0e22e6c7bf4bf7ba54f24f616", + "kind": "contains", + "framework": "react-router", + "source": { + "file": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "startByte": 228, + "endByte": 274, + "startLine": 9, + "startColumn": 2, + "endLine": 9, + "endColumn": 48 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "startByte": 0, + "endByte": 291, + "startLine": 1, + "startColumn": 0, + "endLine": 12, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:3c0731a84fa2ef12843d75ebae20556eda6a3372c1756380c1de41b5b24e5cdb", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/api/items/[id].ts", + "startByte": 0, + "endByte": 143, + "startLine": 1, + "startColumn": 0, + "endLine": 8, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:5ae11324f64f9a609b5a24016f510b99f4f51ea0a2860061408943c409c8dbc4", + "kind": "contains", + "framework": "nuxt", + "source": { + "file": "fixtures/code-graph/routes/typescript/nuxt/server/api/users.get.ts", + "startByte": 0, + "endByte": 54, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/nuxt/server/api/users/[id].post.ts", + "startByte": 0, + "endByte": 57, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:5e07c1998ceb2f807df7ef57d15c19b0ba1c97364ac7c4cb7b0768e10387557b", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/users/[id].ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:74b5ddc96a4ba0ec2403b0a4ad937f2ba30d36792459bbaa69a607d40a39a775", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/api/items/[id].ts", + "startByte": 0, + "endByte": 143, + "startLine": 1, + "startColumn": 0, + "endLine": 8, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:8aacce23b5a2487d3bb46f47f728bb0ab266cfc21d6449cf9b63cc2e25fb79f6", + "kind": "contains", + "framework": "next", + "source": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/page.tsx", + "startByte": 0, + "endByte": 123, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/layout.tsx", + "startByte": 0, + "endByte": 125, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:af33acc5c2410bfe410a84dcd588d63735560f116d81da4ceb1c7c369c5dc79f", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/files/[...rest].astro", + "startByte": 0, + "endByte": 56, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:bb46aca40a48532523fe60b675b4535a3e21c26156e42364bf3b70d4ed6c1c57", + "kind": "contains", + "framework": "sveltekit", + "source": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "startByte": 0, + "endByte": 75, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:bc51e7d1d4870e3569f5e8074fcddd5fcd8bca5ba508e5c18f330aef59420911", + "kind": "contains", + "framework": "next", + "source": { + "file": "fixtures/code-graph/frontend-react/src/app/page.tsx", + "startByte": 0, + "endByte": 118, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/app/layout.tsx", + "startByte": 0, + "endByte": 131, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:be0806a621daff555e8e36c37ba605edfbf50402e8d893ab6360c06feb84182c", + "kind": "contains", + "framework": "react-router", + "source": { + "file": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "startByte": 0, + "endByte": 291, + "startLine": 1, + "startColumn": 0, + "endLine": 12, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "startByte": 228, + "endByte": 274, + "startLine": 9, + "startColumn": 2, + "endLine": 9, + "endColumn": 48 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:d46d3de0405622e796a7a6f0dcc243408431e4164c43d12c8a32847aa3b5b02d", + "kind": "contains", + "framework": "sveltekit", + "source": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "startByte": 0, + "endByte": 61, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "startByte": 0, + "endByte": 75, + "startLine": 1, + "startColumn": 0, + "endLine": 6, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + }, + { + "edgeId": "sha256:f3ec5a2fafccd62c8b555db443547e3846923fb5101e9a26d70dadc737902266", + "kind": "contains", + "framework": "astro", + "source": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "startByte": 0, + "endByte": 15, + "startLine": 1, + "startColumn": 0, + "endLine": 2, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/routes/typescript/astro/src/pages/blog/[slug].astro", + "startByte": 0, + "endByte": 55, + "startLine": 1, + "startColumn": 0, + "endLine": 5, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + } + ], + "added": [ + { + "edgeId": "sha256:7967424549b29c267d115cd60a4426bb1cf23abf50811f98ae62346f24f49388", + "kind": "contains", + "framework": "next", + "source": { + "file": "fixtures/code-graph/frontend-react/src/app/layout.tsx", + "startByte": 0, + "endByte": 131, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "target": { + "file": "fixtures/code-graph/frontend-react/src/app/admin/layout.tsx", + "startByte": 0, + "endByte": 125, + "startLine": 1, + "startColumn": 0, + "endLine": 4, + "endColumn": 0 + }, + "rule": "framework-route-hierarchy" + } + ], + "nodesUnchangedExceptCommunity": true, + "commonEdgesUnchanged": true + }, + "fixtureAddition": { + "files": [ + "fixtures/code-graph/frontend-react/src/app/layout.tsx", + "fixtures/code-graph/frontend-react/src/app/admin/layout.tsx" + ], + "addedNodes": 16, + "addedEdges": 24, + "removedNodes": 0, + "removedEdges": 0, + "note": "Two layout modules add declarations, parameters, default exports, type references, Vite glob imports, route targets, and four containing layout relationships. Source additions are kept separate from the identical-input production comparison." + }, + "semanticAssertions": { + "negativeGroups": 18, + "restoredFalseEdgesIndividuallyRejected": [ + "sha256:3aa44343cb0774ab19678aab3ea377d69781a9b0e22e6c7bf4bf7ba54f24f616", + "sha256:3c0731a84fa2ef12843d75ebae20556eda6a3372c1756380c1de41b5b24e5cdb", + "sha256:4e8881635f480ed920670d90fa8d2c3051b9d98b5d059349e87b8d29ad85d84e", + "sha256:5ae11324f64f9a609b5a24016f510b99f4f51ea0a2860061408943c409c8dbc4", + "sha256:5e07c1998ceb2f807df7ef57d15c19b0ba1c97364ac7c4cb7b0768e10387557b", + "sha256:74b5ddc96a4ba0ec2403b0a4ad937f2ba30d36792459bbaa69a607d40a39a775", + "sha256:af33acc5c2410bfe410a84dcd588d63735560f116d81da4ceb1c7c369c5dc79f", + "sha256:bb46aca40a48532523fe60b675b4535a3e21c26156e42364bf3b70d4ed6c1c57", + "sha256:be0806a621daff555e8e36c37ba605edfbf50402e8d893ab6360c06feb84182c", + "sha256:d46d3de0405622e796a7a6f0dcc243408431e4164c43d12c8a32847aa3b5b02d", + "sha256:ecfc5aa101e90cb633af15515c78217dce4aa5e4ceb17ab4361dbc70e490bd14", + "sha256:f3ec5a2fafccd62c8b555db443547e3846923fb5101e9a26d70dadc737902266" + ], + "exactLayoutEdges": 4, + "missingLayoutEdgesIndividuallyRejected": 4 + }, + "policyChanges": [ + { + "category": "minimums", + "metric": "communities", + "beforeMetric": 232, + "afterMetric": 239, + "beforeBound": 232, + "afterBound": 239, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "edges", + "beforeMetric": 1270, + "afterMetric": 1282, + "beforeBound": 1265, + "afterBound": 1277, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactCrossFileEdges", + "beforeMetric": 62, + "afterMetric": 56, + "beforeBound": 62, + "afterBound": 56, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactCrossFileEdgesPerThousandNodes", + "beforeMetric": 48, + "afterMetric": 43, + "beforeBound": 48, + "afterBound": 43, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactEdgeBearingNodePermille", + "beforeMetric": 710, + "afterMetric": 711, + "beforeBound": 709, + "afterBound": 710, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactEdgeBearingNodes", + "beforeMetric": 906, + "afterMetric": 919, + "beforeBound": 905, + "afterBound": 918, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactEdges", + "beforeMetric": 951, + "afterMetric": 961, + "beforeBound": 946, + "afterBound": 956, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactUniqueTypedEndpointPairs", + "beforeMetric": 943, + "afterMetric": 953, + "beforeBound": 938, + "afterBound": 948, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "exactUniqueTypedEndpointPairsPerThousandNodes", + "beforeMetric": 739, + "afterMetric": 737, + "beforeBound": 735, + "afterBound": 733, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "nodes", + "beforeMetric": 1276, + "afterMetric": 1292, + "beforeBound": 1276, + "afterBound": 1292, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "minimums", + "metric": "uniqueTypedEndpointPairs", + "beforeMetric": 1248, + "afterMetric": 1260, + "beforeBound": 1243, + "afterBound": 1255, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "communities", + "beforeMetric": 232, + "afterMetric": 239, + "beforeBound": 233, + "afterBound": 240, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "connectedComponents", + "beforeMetric": 229, + "afterMetric": 236, + "beforeBound": 230, + "afterBound": 237, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "exactConnectedComponents", + "beforeMetric": 503, + "afterMetric": 512, + "beforeBound": 504, + "afterBound": 513, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "exactIsolatedNodes", + "beforeMetric": 370, + "afterMetric": 373, + "beforeBound": 371, + "afterBound": 374, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "isolatedNodes", + "beforeMetric": 98, + "afterMetric": 99, + "beforeBound": 99, + "afterBound": 100, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "maximums", + "metric": "singletonCommunities", + "beforeMetric": 98, + "afterMetric": 99, + "beforeBound": 99, + "afterBound": 100, + "reason": "Measured correction on unchanged original corpus plus separately reviewed addition of two layout fixtures; previous bound margin retained." + }, + { + "category": "relationshipMinimums", + "relation": "contains", + "metric": "exactCrossFileEdges", + "beforeMetric": 12, + "afterMetric": 4, + "beforeBound": 12, + "afterBound": 4 + }, + { + "category": "relationshipMinimums", + "relation": "imports", + "metric": "exactCrossFileEdges", + "beforeMetric": 12, + "afterMetric": 14, + "beforeBound": 12, + "afterBound": 14 + }, + { + "category": "relationshipMinimums", + "relation": "imports", + "metric": "exactUniqueEndpointPairs", + "beforeMetric": 29, + "afterMetric": 31, + "beforeBound": 29, + "afterBound": 31 + }, + { + "category": "relationshipMinimums", + "relation": "references", + "metric": "exactUniqueEndpointPairs", + "beforeMetric": 49, + "afterMetric": 51, + "beforeBound": 49, + "afterBound": 51 + }, + { + "category": "relationshipMinimums", + "relation": "routes_to", + "metric": "exactUniqueEndpointPairs", + "beforeMetric": 113, + "afterMetric": 115, + "beforeBound": 111, + "afterBound": 113 + } + ], + "artifactSha256": { + "semantic-route-parent-fixture-delta/original-delta.json": "7ba3a6dc0c07c1d37bf9f1db588c2df6fe0d8eb21256986e4c67f201eaa1d5b9", + "semantic-route-parent-fixture-delta/expanded-delta.json": "a0ae2b90b86d4fe6b511d1b94be2a73cb69cd853e1524a44e4b8d961a057ae46", + "semantic-route-parent-fixture-delta/fixture-addition-delta.json": "7ebfc69c21c62ff8da71e68adc38542793a947f974d25eac4dbad9b6454e0e1a", + "semantic-route-parent-fixture-delta/source-inventories.json": "2fbdb4d3b12e29b00324832ac2710f2b29b427f15a51342ad7b67f315cb191fb" + } +} diff --git a/crates/compass-languages/src/frameworks/mod.rs b/crates/compass-languages/src/frameworks/mod.rs index fea62bb44..db5a77b2b 100644 --- a/crates/compass-languages/src/frameworks/mod.rs +++ b/crates/compass-languages/src/frameworks/mod.rs @@ -82,7 +82,7 @@ type TemplateDetector = /// separate from the language producer version: changing framework activation, /// descriptor capabilities, resolution/publication, or resource limits must /// invalidate framework facts without changing the parser/evidence producer. -pub const FRAMEWORK_PACK_SEMANTICS_VERSION: &str = "compass.framework-packs/7"; +pub const FRAMEWORK_PACK_SEMANTICS_VERSION: &str = "compass.framework-packs/8"; /// The concrete implementation stored behind one framework-pack seam. /// diff --git a/crates/compass-resolve/src/frameworks/routes.rs b/crates/compass-resolve/src/frameworks/routes.rs index c38dc5706..e5c0d94b8 100644 --- a/crates/compass-resolve/src/frameworks/routes.rs +++ b/crates/compass-resolve/src/frameworks/routes.rs @@ -1,3 +1,5 @@ +mod hierarchy; + use std::collections::BTreeMap; use std::path::Path; @@ -218,91 +220,25 @@ pub fn publish_resolved_routes( .map(|edge| (edge.source.clone(), edge.target.clone())) .collect::>(); let mut hierarchy_diagnostics = Vec::new(); - let mut route_sources_by_scope = - BTreeMap::<(String, String), Vec<(String, String, RawFrameworkAnchor, bool)>>::new(); - for (route_id, route) in &route_ids { - if !has_filesystem_route_convention(route) { - continue; - } - route_sources_by_scope - .entry((route.framework.clone(), route_hierarchy_scope(route))) - .or_default() - .push(( - route.anchor.source_file.clone(), - route_id.clone(), - route.anchor.clone(), - route - .stages - .iter() - .any(|stage| stage.role == RawRouteStageRole::RouteComponent), - )); - } - for candidates in route_sources_by_scope.values_mut() { - candidates.sort_by(|left, right| left.0.cmp(&right.0)); - } - // Parent lookup is on the hot path for large convention route trees. A - // direct scan of every route in a scope makes a corpus with many routes - // in the same source file quadratic (the synthetic enterprise guard uses - // exactly that shape). Keep the original candidate ordering, but index - // the distinct source files by their parent directory once per scope. - let mut route_parent_sources_by_scope = - BTreeMap::<(String, String), BTreeMap>>::new(); - for ((framework, scope), candidates) in &route_sources_by_scope { - let mut by_directory = BTreeMap::>::new(); - for (source, _, _, _) in candidates { - let portable = source.replace('\\', "/"); - let Some((directory, _)) = portable.rsplit_once('/') else { - continue; - }; - let sources = by_directory.entry(directory.to_owned()).or_default(); - if sources.last().is_none_or(|previous| previous != source) { - sources.push(source.clone()); - } - } - route_parent_sources_by_scope.insert((framework.clone(), scope.clone()), by_directory); - } + let hierarchy = hierarchy::FileRouteHierarchy::new(&route_ids); for (child_id, child) in &route_ids { - if !has_filesystem_route_convention(child) { + let Some(candidates) = hierarchy.candidates(child) else { continue; - } - let scope = route_hierarchy_scope(child); - let mut selected = None; - if let Some(candidates) = - route_sources_by_scope.get(&(child.framework.clone(), scope.clone())) - && let Some(parent_sources) = - route_parent_sources_by_scope.get(&(child.framework.clone(), scope.clone())) - && let Some(parent_source) = - route_parent_source_file_indexed(&child.anchor.source_file, parent_sources) - { - let matching = candidates - .iter() - .filter(|(source, _, _, _)| source == &parent_source) - .collect::>(); - if matching.len() == 1 { - let (_, parent_id, parent_anchor, component) = matching[0]; - selected = Some((parent_id.clone(), parent_anchor.clone(), *component)); - } else if matching.len() > 1 { - let component_candidates = matching - .iter() - .filter(|(_, _, _, component)| *component) - .collect::>(); - if component_candidates.len() == 1 { - let (_, parent_id, parent_anchor, component) = component_candidates[0]; - selected = Some((parent_id.clone(), parent_anchor.clone(), *component)); - } else { - hierarchy_diagnostics.push(json!({ - "kind": "ambiguous_route_parent", - "framework": child.framework, - "sourceFile": child.anchor.source_file, - "parentSourceFile": parent_source, - "candidates": matching.iter().map(|(_, id, _, _)| id).collect::>(), - })); - } - } - } - let Some((parent_id, parent_anchor, _)) = selected else { + }; + let [parent_index] = candidates else { + hierarchy_diagnostics.push(json!({ + "kind": "ambiguous_route_parent", + "framework": child.framework, + "sourceFile": child.anchor.source_file, + "parentSourceFiles": candidates.iter() + .map(|index| &route_ids[*index].1.anchor.source_file).collect::>(), + "candidates": candidates.iter().map(|index| &route_ids[*index].0).collect::>(), + })); continue; }; + let (parent_id, parent) = &route_ids[*parent_index]; + let parent_id = parent_id.clone(); + let parent_anchor = &parent.anchor; if parent_id == *child_id || !existing_hierarchy.insert((parent_id.clone(), child_id.clone())) { @@ -342,7 +278,7 @@ pub fn publish_resolved_routes( ]); attributes.insert( "parent_source_anchor".into(), - serde_json::to_value(source_anchor(&parent_anchor)).unwrap_or(Value::Null), + serde_json::to_value(source_anchor(parent_anchor)).unwrap_or(Value::Null), ); hierarchy_edges.push(RawEdgeRecord { source: parent_id, @@ -383,52 +319,6 @@ fn route_node_id(route: &RawRouteFact) -> String { ]) } -/// Select the same physical parent route that the independent frontend oracle -/// uses: walk up the source directory tree and take the first route module in -/// that directory's deterministic order. This preserves file-based route -/// conventions (including same-path layouts and TanStack dot segments) that -/// cannot be reconstructed from a normalized URL alone. -#[cfg(test)] -fn route_parent_source_file( - child_source: &str, - candidates: &[(String, String, RawFrameworkAnchor, bool)], -) -> Option { - let mut by_directory = BTreeMap::>::new(); - for (source, _, _, _) in candidates { - let portable = source.replace('\\', "/"); - let Some((directory, _)) = portable.rsplit_once('/') else { - continue; - }; - let sources = by_directory.entry(directory.to_owned()).or_default(); - if sources.last().is_none_or(|previous| previous != source) { - sources.push(source.clone()); - } - } - route_parent_source_file_indexed(child_source, &by_directory) -} - -fn route_parent_source_file_indexed( - child_source: &str, - sources_by_directory: &BTreeMap>, -) -> Option { - let portable = child_source.replace('\\', "/"); - let parts = portable.split('/').collect::>(); - if parts.len() < 2 { - return None; - } - for index in (1..parts.len()).rev() { - let parent_directory = parts[..index].join("/"); - if let Some(candidates) = sources_by_directory.get(&parent_directory) - && let Some(source) = candidates - .iter() - .find(|source| source.replace('\\', "/") != portable) - { - return Some(source.clone()); - } - } - None -} - fn has_filesystem_route_convention(route: &RawRouteFact) -> bool { // Receiver spellings such as `r` are local bindings, not filesystem tree // identities. Only facts from the explicit file-route producers may use @@ -477,15 +367,14 @@ fn route_hierarchy_scope(route: &RawRouteFact) -> String { .replace('\\', "/") .trim_matches('/') .to_owned(); - for marker in [ - "src/app/", - "app/", - "src/pages/", - "pages/", - "app/routes/", - "src/routes/", - "routes/", - ] { + let markers: &[&str] = match route.framework.as_str() { + "next" => &["src/app/", "app/", "src/pages/", "pages/"], + "react-router" | "remix" => &["app/routes/", "src/routes/", "routes/"], + "tanstack-router" | "sveltekit" => &["src/routes/", "routes/"], + "nuxt" => &["app/pages/", "src/pages/", "pages/"], + _ => &[], + }; + for &marker in markers { let mut offset = 0; while let Some(found) = source.get(offset..).and_then(|suffix| suffix.find(marker)) { let index = offset + found; @@ -1370,13 +1259,9 @@ mod tests { fn route_hierarchy_never_selects_the_route_as_its_own_parent() { let source = "src/routes/home.tsx"; let route = route_for_scope(source); - let candidates = vec![( - source.to_owned(), - "route-id".to_owned(), - route.anchor, - false, - )]; - assert_eq!(route_parent_source_file(source, &candidates), None); + let candidates = vec![("route-id".to_owned(), route.clone())]; + let hierarchy = hierarchy::FileRouteHierarchy::new(&candidates); + assert_eq!(hierarchy.candidates(&route), None); } #[test] diff --git a/crates/compass-resolve/src/frameworks/routes/hierarchy.rs b/crates/compass-resolve/src/frameworks/routes/hierarchy.rs new file mode 100644 index 000000000..426633ea2 --- /dev/null +++ b/crates/compass-resolve/src/frameworks/routes/hierarchy.rs @@ -0,0 +1,225 @@ +//! File-route parentage from framework naming rules, never directory ordering. + +use std::collections::BTreeMap; + +use compass_languages::RawRouteFact; + +use super::{has_filesystem_route_convention, route_hierarchy_scope}; + +pub(super) struct FileRouteHierarchy { + by_key: BTreeMap<(String, String, String), Vec>, +} + +impl FileRouteHierarchy { + pub(super) fn new(routes: &[(String, RawRouteFact)]) -> Self { + let mut by_key = BTreeMap::<_, Vec>::new(); + for (index, (_, route)) in routes.iter().enumerate() { + let Some(path) = RoutePath::for_route(route) else { + continue; + }; + if path.can_parent && matches!(route.operation.as_str(), "PAGE" | "ROOT") { + by_key + .entry((route.framework.clone(), path.scope, path.parts.join("/"))) + .or_default() + .push(index); + } + } + for candidates in by_key.values_mut() { + candidates.sort_by(|left, right| routes[*left].0.cmp(&routes[*right].0)); + candidates.dedup_by(|left, right| routes[*left].0 == routes[*right].0); + } + Self { by_key } + } + + /// Return every candidate at the nearest semantic parent key. An ambiguous + /// key must not fall back to a more distant parent or an arbitrary file. + pub(super) fn candidates(&self, child: &RawRouteFact) -> Option<&[usize]> { + let path = RoutePath::for_route(child)?; + let first = path.first_parent_len?; + for length in (path.minimum_parent_len..=first).rev() { + let key = ( + child.framework.clone(), + path.scope.clone(), + path.parts[..length].join("/"), + ); + if let Some(candidates) = self.by_key.get(&key) { + return Some(candidates); + } + } + None + } +} + +struct RoutePath { + scope: String, + parts: Vec, + can_parent: bool, + first_parent_len: Option, + minimum_parent_len: usize, +} + +impl RoutePath { + fn for_route(route: &RawRouteFact) -> Option { + if !has_filesystem_route_convention(route) { + return None; + } + let scope = route_hierarchy_scope(route); + let portable = route.anchor.source_file.replace('\\', "/"); + let relative = portable + .trim_matches('/') + .strip_prefix(&format!("{scope}/"))?; + let (stem, extension) = relative.rsplit_once('.')?; + if stem + .split('/') + .any(|part| part.is_empty() || matches!(part, "." | "..")) + { + return None; + } + let mut parts = stem.split('/').map(str::to_owned).collect::>(); + let file = parts.last()?.as_str(); + let (can_parent, first_parent_len, minimum_parent_len) = match route.framework.as_str() { + "next" => { + // Pages Router and HTTP route handlers have no automatic + // layout parent inferred from their URL or source directory. + if route.rule.as_deref() != Some("next-app-router-convention") + || !matches!( + extension, + "ts" | "tsx" | "js" | "jsx" | "mts" | "cts" | "mjs" | "cjs" + ) + || !matches!( + file, + "layout" + | "page" + | "template" + | "error" + | "loading" + | "not-found" + | "default" + ) + { + return None; + } + let layout = file == "layout"; + parts.pop(); + ( + layout, + if layout { + parts.len().checked_sub(1) + } else { + Some(parts.len()) + }, + 0, + ) + } + "sveltekit" => { + if extension != "svelte" || !matches!(file, "+layout" | "+page" | "+error") { + return None; + } + let layout = file == "+layout"; + parts.pop(); + ( + layout, + if layout { + parts.len().checked_sub(1) + } else { + Some(parts.len()) + }, + 0, + ) + } + "nuxt" => { + if extension != "vue" || route.rule.as_deref() != Some("nuxt-file-route-convention") + { + return None; + } + // parent.vue owns parent/child.vue; index.vue is a leaf. + // Named-view and route-group configuration is not represented + // in these facts, so it cannot establish a parent here. + if parts + .iter() + .any(|part| part.contains('@') || part.starts_with('(')) + { + return None; + } + (file != "index", parts.len().checked_sub(1), 1) + } + "react-router" | "remix" | "tanstack-router" => { + if !matches!( + extension, + "ts" | "tsx" | "js" | "jsx" | "mts" | "cts" | "mjs" | "cjs" + ) { + return None; + } + let tanstack = route.framework == "tanstack-router"; + if !tanstack && parts.len() > 1 { + // Flat-route folders expose route.tsx; colocated helper + // modules do not define nested routes by directory alone. + if parts.len() != 2 || file != "route" { + return None; + } + parts.pop(); + } + parts = flat_segments(&parts.join("/"), tanstack)?; + if tanstack && parts.last().is_some_and(|part| part == "route") { + parts.pop(); + } + if tanstack && parts == ["__root"] { + parts.clear(); + (true, None, 0) + } else { + let last = parts.last()?; + let can_parent = + last != if tanstack { "index" } else { "_index" } && !last.ends_with('_'); + (can_parent, parts.len().checked_sub(1), 0) + } + } + // Astro layouts are explicit component composition. File routes + // and API endpoints alone prove no containing route. + _ => return None, + }; + Some(Self { + scope, + parts, + can_parent, + first_parent_len, + minimum_parent_len, + }) + } +} + +/// Preserve escaped dots as literal filename text. Index, pathless, and +/// non-nesting markers remain in keys, preventing URL normalization from +/// merging distinct route ownership. Every iteration consumes one byte. +fn flat_segments(stem: &str, tanstack: bool) -> Option> { + let mut segments = Vec::new(); + let mut start = 0; + let mut escaped = false; + for (index, byte) in stem.bytes().enumerate() { + match byte { + b'[' if !escaped => escaped = true, + b']' if escaped => escaped = false, + b'[' | b']' => return None, + b'.' | b'/' if !escaped => { + segments.push(stem.get(start..index)?.to_owned()); + start = index + 1; + } + _ => {} + } + } + if escaped { + return None; + } + segments.push(stem.get(start..)?.to_owned()); + if segments.iter().any(|segment| segment.is_empty()) { + return None; + } + if tanstack { + if segments.iter().any(|segment| segment.starts_with('-')) + || segments.last().is_some_and(|segment| segment == "lazy") + { + return None; + } + segments.retain(|segment| !(segment.starts_with('(') && segment.ends_with(')'))); + } + Some(segments) +} diff --git a/crates/compass-resolve/tests/framework_routes.rs b/crates/compass-resolve/tests/framework_routes.rs index baa4b8c78..6fc5a9688 100644 --- a/crates/compass-resolve/tests/framework_routes.rs +++ b/crates/compass-resolve/tests/framework_routes.rs @@ -161,6 +161,310 @@ fn filesystem_parentage_requires_a_recognized_convention() -> Result<(), Box Result<(), Box> { + type Case<'a> = (&'a str, &'a [&'a str], &'a [(&'a str, &'a str)]); + let cases: &[Case<'_>] = &[ + ( + "next", + &[ + "app/page.tsx", + "app/admin/page.tsx", + "app/admin/settings/page.tsx", + ], + &[], + ), + ( + "next", + &[ + "app/layout.tsx", + "app/page.tsx", + "app/blog/layout.tsx", + "app/blog/page.tsx", + "app/blog/item/page.tsx", + "app/api/route.ts", + "app/global-error.tsx", + ], + &[ + ("app/layout.tsx", "app/page.tsx"), + ("app/layout.tsx", "app/blog/layout.tsx"), + ("app/blog/layout.tsx", "app/blog/page.tsx"), + ("app/blog/layout.tsx", "app/blog/item/page.tsx"), + ], + ), + ( + "next", + &["app/layout.tsx", "app/layout.jsx", "app/page.tsx"], + &[], + ), + ("next", &["one/app/layout.tsx", "two/app/page.tsx"], &[]), + ( + "next", + &["pages/index.tsx", "pages/blog.tsx", "pages/blog/post.tsx"], + &[], + ), + ( + "next", + &[ + "app/layout.tsx", + "app/(private)/layout.tsx", + "app/(private)/page.tsx", + ], + &[ + ("app/layout.tsx", "app/(private)/layout.tsx"), + ("app/(private)/layout.tsx", "app/(private)/page.tsx"), + ], + ), + ( + "react-router", + &["src/routes/home.tsx", "src/routes/tanstack.tsx"], + &[], + ), + ( + "react-router", + &[ + "app/routes/concerts.tsx", + "app/routes/concerts.$city.tsx", + "app/routes/concerts._index.tsx", + "app/routes/concerts_.mine.tsx", + ], + &[ + ("app/routes/concerts.tsx", "app/routes/concerts.$city.tsx"), + ("app/routes/concerts.tsx", "app/routes/concerts._index.tsx"), + ], + ), + ( + "react-router", + &[ + "app/routes/_auth.tsx", + "app/routes/_auth.login.tsx", + "app/routes/_index.tsx", + "app/routes/about.tsx", + ], + &[("app/routes/_auth.tsx", "app/routes/_auth.login.tsx")], + ), + ( + "remix", + &[ + "app/routes/concerts/route.tsx", + "app/routes/concerts.$city.tsx", + "app/routes/concerts/helper.tsx", + ], + &[( + "app/routes/concerts/route.tsx", + "app/routes/concerts.$city.tsx", + )], + ), + ( + "remix", + &[ + "app/routes/concerts.tsx", + "app/routes/concerts/route.tsx", + "app/routes/concerts.$city.tsx", + ], + &[], + ), + ( + "react-router", + &[ + "app/routes/a.tsx", + "app/routes/a.b.tsx", + "app/routes/a.b_.c.tsx", + ], + &[ + ("app/routes/a.tsx", "app/routes/a.b.tsx"), + ("app/routes/a.tsx", "app/routes/a.b_.c.tsx"), + ], + ), + ( + "react-router", + &[ + "app/routes/foo[.]bar.tsx", + "app/routes/foo[.]bar.child.tsx", + "app/routes/foo.tsx", + ], + &[("app/routes/foo[.]bar.tsx", "app/routes/foo[.]bar.child.tsx")], + ), + ( + "react-router", + &[ + "app/routes/a.tsx", + "app/routes/a.jsx", + "app/routes/a.child.tsx", + ], + &[], + ), + ( + "tanstack-router", + &[ + "src/routes/__root.tsx", + "src/routes/index.tsx", + "src/routes/posts.tsx", + "src/routes/posts.index.tsx", + "src/routes/posts.$id.tsx", + "src/routes/posts_.$id.edit.tsx", + ], + &[ + ("src/routes/__root.tsx", "src/routes/index.tsx"), + ("src/routes/__root.tsx", "src/routes/posts.tsx"), + ("src/routes/posts.tsx", "src/routes/posts.index.tsx"), + ("src/routes/posts.tsx", "src/routes/posts.$id.tsx"), + ("src/routes/__root.tsx", "src/routes/posts_.$id.edit.tsx"), + ], + ), + ( + "tanstack-router", + &[ + "src/routes/account/route.tsx", + "src/routes/account/overview.tsx", + "src/routes/account/index.tsx", + "src/routes/account/-helper.tsx", + "src/routes/account/route.lazy.tsx", + ], + &[ + ( + "src/routes/account/route.tsx", + "src/routes/account/overview.tsx", + ), + ( + "src/routes/account/route.tsx", + "src/routes/account/index.tsx", + ), + ], + ), + ( + "tanstack-router", + &["src/routes/_auth.tsx", "src/routes/(group)/_auth.login.tsx"], + &[("src/routes/_auth.tsx", "src/routes/(group)/_auth.login.tsx")], + ), + ( + "tanstack-router", + &["src/routes/index.tsx", "src/routes/about.tsx"], + &[], + ), + ( + "nuxt", + &[ + "pages/index.vue", + "pages/parent.vue", + "pages/parent/child.vue", + "pages/parent/index.vue", + "pages/other.vue", + ], + &[ + ("pages/parent.vue", "pages/parent/child.vue"), + ("pages/parent.vue", "pages/parent/index.vue"), + ], + ), + ( + "nuxt", + &[ + "pages/parent/index.vue", + "pages/parent/child.vue", + "server/api/users.get.ts", + "server/api/users/[id].post.ts", + ], + &[], + ), + ( + "sveltekit", + &["src/routes/+page.svelte", "src/routes/+server.ts"], + &[], + ), + ( + "sveltekit", + &[ + "src/routes/+layout.svelte", + "src/routes/+page.svelte", + "src/routes/blog/+layout.svelte", + "src/routes/blog/+page.svelte", + "src/routes/blog/+server.ts", + ], + &[ + ("src/routes/+layout.svelte", "src/routes/+page.svelte"), + ( + "src/routes/+layout.svelte", + "src/routes/blog/+layout.svelte", + ), + ( + "src/routes/blog/+layout.svelte", + "src/routes/blog/+page.svelte", + ), + ], + ), + ( + "astro", + &[ + "src/pages/about.astro", + "src/pages/blog/post.astro", + "src/pages/api/items.ts", + ], + &[], + ), + ]; + for (framework, files, expected) in cases { + let facts = files + .iter() + .map(|file| { + let mut fact = route("Page"); + fact.framework = (*framework).to_owned(); + fact.operation = "PAGE".to_owned(); + fact.origin = RawFrameworkOrigin::Convention; + fact.anchor.source_file = (*file).to_owned(); + fact.rule = Some( + match *framework { + "next" if file.starts_with("pages/") => "next-file-route-convention", + "next" if file.ends_with("/route.ts") => "next-app-route-convention", + "next" => "next-app-router-convention", + "react-router" => "react-router-file-route-convention", + "remix" => "remix-route-convention", + "tanstack-router" => "tanstack-file-route-convention", + "nuxt" => "nuxt-file-route-convention", + "sveltekit" => "sveltekit-file-route-convention", + "astro" => "astro-file-route-convention", + _ => "unknown", + } + .to_owned(), + ); + RawFrameworkFact::Route(fact) + }) + .collect::>(); + let expected = expected + .iter() + .map(|(a, b)| ((*a).to_owned(), (*b).to_owned())) + .collect::>(); + for facts in [facts.clone(), facts.into_iter().rev().collect()] { + let mut extraction = Extraction { + framework_facts: facts, + ..Extraction::default() + }; + resolve_and_publish_framework_routes(&mut extraction, FrameworkLimits::default())?; + let mut actual = std::collections::BTreeSet::new(); + for edge in extraction + .edges + .iter() + .filter(|edge| edge.string("relation") == "contains") + { + let source = extraction + .nodes + .iter() + .find(|node| node.id == edge.source) + .ok_or("missing hierarchy source")?; + let target = extraction + .nodes + .iter() + .find(|node| node.id == edge.target) + .ok_or("missing hierarchy target")?; + actual.insert((source.string("source_file"), target.string("source_file"))); + assert_eq!(edge.string("rule"), "framework-route-hierarchy"); + } + assert_eq!(actual, expected, "{framework}: {files:?}"); + } + } + Ok(()) +} + #[test] fn neutral_framework_roles_publish_existing_node_roles_and_reject_unknown_values() -> Result<(), Box> { diff --git a/docs/design/code-graph-v1-qualification.md b/docs/design/code-graph-v1-qualification.md index 731183034..6e9067a00 100644 --- a/docs/design/code-graph-v1-qualification.md +++ b/docs/design/code-graph-v1-qualification.md @@ -46,10 +46,19 @@ previous margin. Counts alone do not validate edge meaning or community quality. The [fixture review](../../benchmarks/agent_query/route_hierarchy_fixture_review.json) records all removed identities, source hashes, and policy changes; eight new semantic negatives reject the former links independently of those counts. -The current route-hierarchy checkpoint still fails one of these negatives: +That route-hierarchy checkpoint still failed one of these negatives: the directory-order lookup makes sibling `/tanstack` and `/home` modules a -parent-child pair. This is an unresolved production defect; the recalibrated -topology counts do not constitute a qualification pass. +parent-child pair. The subsequent framework-specific correction removes the +remaining 12 unsupported file-route records from identical original sources. +The negative manifest now covers 18 source-file pairs. Two explicit Next layout +fixtures separately add four expected layout relationships, checked as an exact +set by the frontend fixture gate. The +[follow-up review](../../benchmarks/agent_query/semantic_route_parent_fixture_review.json) +records both identical-input comparisons and the fixture-only delta, along with +mutation checks and topology bound adjustments preserving their prior margins. +These focused checks pass; the complete fixture gate is pending. Historical +pinned frontend hierarchy scorecards are invalidated until their sources are +re-reviewed, so their prior counts cannot establish current qualification. ## Command diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 9ad3c805b..05417091c 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1423,6 +1423,87 @@ the new assertion. This mutation exercise validates the checker; it is not a production correction. Existing native results describe the unchanged Rust production code, not a fix for this newly exposed defect. +#### Framework-specific filesystem parents and independent source review + +The next correction replaces directory-order selection with an indexed lookup +of framework parent roles: Next layouts, the supported Svelte layout facts, +Nuxt parent pages, and default React Router/Remix/TanStack flat-route names. +Pages, standalone HTTP endpoints, and Astro routes no longer establish parents +by proximity. Ambiguous nearest parents remain unresolved. Custom configuration +and extraction gaps are documented in the framework reference; this change does +not claim complete routing support. Framework semantics advance to version 8; +the product version remains 0.3.30 and historical graphs remain immutable. + +The frozen binary SHA256 is +`9efe548537b106c69d1da7a5ac7955e764e43103e0d1e4a754a3c1880c292bea`. +On the **identical original fixture corpus**, it removes exactly **12 more +unsupported containment records**, adds none, and retains all **1,276 nodes** +unchanged except for community assignments. All common edges are unchanged. +The removed records are two React Router sibling links, two Next page-parent +links, five Astro links, one Nuxt endpoint link, and two Svelte page/server +links. The source-reviewed negative manifest now covers **18 file pairs** and +passes on the production graph. Restoring each of these 12 false records +individually makes the checker fail. + +Adding two explicit Next layout fixtures is measured separately: the sources +add 16 nodes and 24 edges to the corrected original graph, including four +expected layout relationships. On identical expanded sources, old versus new +production removes 12 false links and adds the missing root-to-nested-layout +link. No other common edge or node content changes. The frontend fixture gate +requires exactly the four source-specified layout pairs; omitting any one fails +its mutation check. The topology bounds move by the measured original-before to +expanded-after deltas, preserving every previous margin. The +[complete fixture review](../../benchmarks/agent_query/semantic_route_parent_fixture_review.json) +separates the production delta, fixture addition, mutation checks, and each +policy adjustment. Increased fragmentation is not evidence of better cohesion. + +The JavaScript source oracle now uses a separate pairwise convention predicate, +with 15 manually specified cases in both input orders. The native route matrix +contains 23 cases, also in both orders. Agreement is still insufficient by +itself: the historical Next, React Router, and TanStack hierarchy scorecards +are explicitly **invalidated**, retaining their old counts and digests until +their sources are independently reviewed. Pinned hierarchy qualification +cannot use those records as passing evidence. + +A real Next diagnostic uses 273 files (1,398,028 bytes) projected read-only from +shadcn/ui commit `a87a63b2ca25143d26c8bd0903e4e9bc77b3f824`. Four positive and +three negative pairs were recorded before inspecting either graph for this +projection. Old production passes **6/7** and corrected production **7/7**. +The complete graph delta contains **13 removed** links (nine to HTTP handlers, +two page-to-layout reversals, and two loading-to-layout reversals) and **four +added** AppLayout-to-nested-layout links. Every changed pair was source-reviewed; +the 14,762 nodes are unchanged except for communities and all common edges are +unchanged. This is a source-selected development diagnostic over a partial +repository, not held-out evidence or a graph-wide precision estimate. + +Fresh paired builds on the unchanged five-language panel produce byte-identical +Compass graphs to the preceding route correction. The same 110 requests yield: + +| Measure | Compass | Graphify | +| --- | ---: | ---: | +| Query text oracle | 46/55 | 46/55 | +| Source-checked paths | 6/10 | 8/10 | +| Selected source relationships | 16/21 | 20/21 | +| All reviewed occurrences, corrected Click oracle | 16/21 | 17/21 | + +The [development review](../../benchmarks/agent_query/semantic_route_parent_development_review.json) +records pins, binary and graph hashes, source witnesses, and raw-artifact hashes. +This correction does not improve the panel scores or prove superiority. The +shared-machine timing run overlaps other verification and supports no speed +claim. MCP scores from the earlier run are not presented as a fresh rerun. + +Verification: **1,436 native tests passed, zero failed, two ignored** before a +trivial Clippy needless-borrow correction; the final production binary built +successfully. Workspace library/binary and selected-integration Clippy passes. +A broader invocation including the untouched `react_frontend` test failed on +39 existing `unwrap`/`expect` lint violations; its failure log is retained. +All **88 script tests**, **15 JavaScript oracle cases**, and **97 benchmark +tests** pass, as do formatting and the product-boundary check. The first full +fixture invocation stopped because its default parser-source path was absent. +A second invocation uses the existing parser bundle in this worktree's target +directory; its complete result remains pending. No full qualification pass is +claimed at this checkpoint. + #### Java constructor receiver diagnostic A separate frozen-binary diagnostic reproduces the jsoup constructor receiver @@ -1439,9 +1520,10 @@ The native evidence and corrected graph inspection agree on the missing calls. ## Next evidence to collect -1. Correct filesystem route-parent selection and its non-independent frontend - oracle; keep the new semantic negative failing until the false parent is - removed by production behavior. Then rerun the full qualification gate. +1. Finish the full qualification run for framework-specific route parents and + re-review the invalidated pinned hierarchy scorecards from their sources. + The corrected fixture and selected real-source evidence above do not replace + those broader checks. 2. Extend source-proven constructor, loop/result/iterator inference to recover the jsoup and fd misses. Keep exact build/source provenance for subsequent release comparisons; diff --git a/docs/reference/react-framework-graph.md b/docs/reference/react-framework-graph.md index d7a248b07..966e2f306 100644 --- a/docs/reference/react-framework-graph.md +++ b/docs/reference/react-framework-graph.md @@ -123,6 +123,33 @@ the owning file-route producer. Programmatic router variables such as `r` or and groups require framework composition evidence. This boundary also applies to programmatic registrations using a framework that supports file routes. +Parent selection uses framework-specific source identities, not normalized URL +prefixes or whichever filename sorts first: + +| Framework | File-parent rule | +| --- | --- | +| Next App Router | The nearest unique `layout` module in the same or an ancestor directory; a nested layout starts searching above itself. HTTP route handlers and `global-error` do not inherit this UI layout relationship. | +| React Router / Remix flat routes | The nearest declared dot-segment prefix. Pathless prefixes remain identities, index files cannot parent siblings, and trailing-underscore opt-outs do not match the ordinary parent. A route folder exposes `route.tsx`; colocated helpers are not nested routes. | +| TanStack default file routes | Dot and directory segments, `route`, `index`, `__root`, pathless names, route groups, and trailing-underscore identities are distinguished. Ignored and lazy companion files do not define parent candidates. | +| Nuxt page routes | `parent.vue` can parent `parent/child.vue`; a sibling or `index.vue` cannot substitute for that declaration. | +| SvelteKit | Only an explicitly published `+layout.svelte` route fact can parent UI files. The current producer does not publish these layout facts, so this rule alone does not add layout coverage. `+server` remains separate. | +| Next Pages Router / Astro / standalone API routes | A source directory or URL prefix establishes no automatic layout parent. Explicit composition remains separate evidence. | + +Multiple candidates for the nearest parent key produce an ambiguity diagnostic +and no containment edge. They do not select the first file or fall back to a +more distant layout. Root scopes separate independent project trees. Custom +route tokens/configuration, unrepresented root modules, and advanced layout +resets require additional evidence; these defaults do not claim full runtime +routing equivalence. + +The naming semantics are documented by +[Next.js](https://nextjs.org/docs/app/getting-started/layouts-and-pages), +[React Router](https://reactrouter.com/how-to/file-route-conventions), +[TanStack Router](https://tanstack.com/router/latest/docs/routing/file-based-routing), +[Nuxt](https://nuxt.com/docs/3.x/directory-structure/pages/), +[SvelteKit](https://svelte.dev/docs/kit/routing), and +[Astro](https://docs.astro.build/en/basics/layouts/). + Generated files, symlinks that escape the owning root, malformed syntax, dynamic imports, computed configuration, and conditional values remain unsupported or incomplete unless a framework pack has independently qualified diff --git a/fixtures/code-graph/frontend-react/src/app/admin/layout.tsx b/fixtures/code-graph/frontend-react/src/app/admin/layout.tsx new file mode 100644 index 000000000..31e11d04e --- /dev/null +++ b/fixtures/code-graph/frontend-react/src/app/admin/layout.tsx @@ -0,0 +1,3 @@ +export default function AdminLayout({ children }: { children: React.ReactNode }) { + return
{children}
; +} diff --git a/fixtures/code-graph/frontend-react/src/app/layout.tsx b/fixtures/code-graph/frontend-react/src/app/layout.tsx new file mode 100644 index 000000000..b3b059dab --- /dev/null +++ b/fixtures/code-graph/frontend-react/src/app/layout.tsx @@ -0,0 +1,3 @@ +export default function RootLayout({ children }: { children: React.ReactNode }) { + return {children}; +} diff --git a/scripts/qualify_react_frontend_graph.py b/scripts/qualify_react_frontend_graph.py index 54a58bb63..2eab5f1db 100755 --- a/scripts/qualify_react_frontend_graph.py +++ b/scripts/qualify_react_frontend_graph.py @@ -880,6 +880,23 @@ def capability_fact_matches(candidate: dict[str, Any], fact: dict[str, Any], cap } +def assert_fixture_route_hierarchy(nodes: list[dict[str, Any]], edges: list[dict[str, Any]]) -> None: + """Hand-reviewed Next layout composition in the checked-in fixture source.""" + index = {node["id"]: node for node in nodes} + actual = { + (value(index[edge["source"]], "framework"), source_file(index[edge["source"]]), source_file(index[edge["target"]])) + for edge in edges + } + expected = { + ("next", "src/app/layout.tsx", "src/app/page.tsx"), + ("next", "src/app/layout.tsx", "src/app/admin/layout.tsx"), + ("next", "src/app/admin/layout.tsx", "src/app/admin/page.tsx"), + ("next", "src/app/admin/layout.tsx", "src/app/admin/settings/page.tsx"), + } + if actual != expected or len(edges) != len(expected): + fail(f"fixture route hierarchy differs from source layout roles: missing={sorted(expected - actual)} unexpected={sorted(actual - expected)} records={len(edges)} expectedRecords={len(expected)}") + + def load_expectations(path: Path | None = None) -> dict[str, Any]: expectations = load(path or EXPECTATIONS) if expectations.get("schema") != "compass.framework-evidence/1": @@ -978,8 +995,7 @@ def check_positive(graph: dict[str, Any], expectations: dict[str, Any]) -> dict[ and edge.get("source") in route_ids and edge.get("target") in route_ids ] - if not hierarchy_edges: - fail("frontend route hierarchy did not publish a route-to-route contains edge") + assert_fixture_route_hierarchy(nodes, hierarchy_edges) if not all( value(edge, "_origin") == "convention" or any( diff --git a/scripts/qualify_react_frontend_graph.sh b/scripts/qualify_react_frontend_graph.sh index 9c33d522b..f5453c5a7 100755 --- a/scripts/qualify_react_frontend_graph.sh +++ b/scripts/qualify_react_frontend_graph.sh @@ -59,6 +59,7 @@ fi } cd "$ROOT" +node --test "$ROOT/scripts/tests/react_route_hierarchy_oracle.test.mjs" BUILD_REVISION="$(git rev-parse HEAD)" echo "[react-frontend] build release production binary ($MODE mode)" PROJECT_ROOT="$PARSER_ROOT" TSLP_OFFLINE=1 CARGO_TARGET_DIR="$TARGET" \ diff --git a/scripts/react_frontend_source_oracle.mjs b/scripts/react_frontend_source_oracle.mjs index 4ca8457bf..bcad61e3c 100644 --- a/scripts/react_frontend_source_oracle.mjs +++ b/scripts/react_frontend_source_oracle.mjs @@ -16,6 +16,7 @@ import process from "node:process"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import ts from "typescript"; +import { HIERARCHY_SEMANTICS, sourceRouteParent, sourceRouteScope } from "./react_route_hierarchy_oracle.mjs"; const SCHEMA = "compass.react-frontend-source-oracle/1"; const PROVIDER = "typescript_compiler_api_5_9_3_frontend_projection"; @@ -821,37 +822,6 @@ function sourceFacts(root, framework, records) { if (framework === "tanstack-router") return /(^|\/)src\/routes\/.+\.[cm]?[jt]sx?$/u.test(file); return false; }; - const routeParent = (file, candidates) => { - const parts = file.replaceAll("\\", "/").split("/"); - for (let index = parts.length - 1; index > 0; index -= 1) { - const parentDirectory = parts.slice(0, index).join("/"); - // Do not select the child itself as its own parent. A route module is - // always a candidate in its own directory, so omitting this identity - // guard would make the loop stop at the first iteration and silently - // drop every ancestor hierarchy relationship. - const parent = candidates.find((candidate) => candidate !== file && candidate.split("/").slice(0, -1).join("/") === parentDirectory); - if (parent) return parent; - } - return null; - }; - const routeHierarchyScope = (file) => { - const normalized = file.replaceAll("\\", "/").replace(/^\/+|\/+$/gu, ""); - for (const marker of [ - "src/app/", - "app/", - "src/pages/", - "pages/", - "app/routes/", - "src/routes/", - "routes/", - ]) { - const index = normalized.indexOf(marker); - if (index >= 0 && (index === 0 || normalized[index - 1] === "/")) { - return `${normalized.slice(0, index)}${marker.slice(0, -1)}`; - } - } - return ""; - }; const resolveSourceFile = (file, moduleSpecifier) => { if (typeof moduleSpecifier !== "string" || !moduleSpecifier || moduleSpecifier.startsWith("@")) return null; const clean = moduleSpecifier.replace(/^\.\//u, ""); @@ -1070,8 +1040,7 @@ function sourceFacts(root, framework, records) { } } - // Compass uses bytewise portable-path ordering at the resolver boundary; - // avoid locale-dependent ordering when selecting a same-directory parent. + // Sort output deterministically; ordering is never evidence of parentage. routeFiles.sort(); const hierarchyCapability = { "next-app": "next.app.hierarchy", @@ -1083,14 +1052,15 @@ function sourceFacts(root, framework, records) { if (hierarchyCapability) { const groups = new Map(); for (const file of routeFiles) { - const scope = routeHierarchyScope(file); + const scope = sourceRouteScope(file, framework); + if (!scope) continue; if (!groups.has(scope)) groups.set(scope, []); groups.get(scope).push(file); } for (const candidates of groups.values()) { candidates.sort(); for (const file of candidates) { - const parent = routeParent(file, candidates); + const parent = sourceRouteParent(file, candidates, framework); if (parent) addHierarchyFact(hierarchyCapability, parent, file); } } @@ -1206,6 +1176,7 @@ function main() { const document = { schema: SCHEMA, provider: PROVIDER, + hierarchySemantics: HIERARCHY_SEMANTICS, toolchain: `node-${process.versions.node.split(".")[0]};typescript-${oracle.header.metadata?.compilerVersion ?? "5.9.3"}`, rootRelative: true, framework, diff --git a/scripts/react_route_hierarchy_oracle.mjs b/scripts/react_route_hierarchy_oracle.mjs new file mode 100644 index 000000000..c7ef7bafd --- /dev/null +++ b/scripts/react_route_hierarchy_oracle.mjs @@ -0,0 +1,79 @@ +/** + * Source-only route-parent oracle. Compare candidate/child relationships from + * documented framework conventions; no Compass records or resolver indexes. + * + * Next: https://nextjs.org/docs/app/getting-started/layouts-and-pages + * Flat routes: https://reactrouter.com/how-to/file-route-conventions + * TanStack: https://tanstack.com/router/latest/docs/routing/file-based-routing + */ + +export const HIERARCHY_SEMANTICS = 2; + +export function sourceRouteScope(file, framework) { + file = file.replaceAll("\\", "/"); + const markers = framework === "next-app" + ? ["src/app/", "app/"] + : framework === "tanstack-router" + ? ["src/routes/", "routes/"] + : ["app/routes/", "src/routes/", "routes/"]; + for (const marker of markers) { + let offset = 0; + while (offset < file.length) { + const index = file.indexOf(marker, offset); + if (index < 0) break; + if (index === 0 || file[index - 1] === "/") return file.slice(0, index + marker.length); + offset = index + 1; + } + } + return null; +} + +function description(file, framework) { + const portable = file.replaceAll("\\", "/"); + const root = sourceRouteScope(portable, framework); + if (!root) return null; + const relative = portable.slice(root.length).replace(/\.[cm]?[jt]sx?$/u, ""); + if (relative === portable.slice(root.length)) return null; + if (framework === "next-app") { + const components = relative.split("/"); + const role = components.pop(); + if (!["layout", "page", "template", "error", "loading", "not-found", "default"].includes(role)) return null; + return { root, parts: components, parent: role === "layout", layout: true }; + } + let identifier = relative; + if (framework !== "tanstack-router" && identifier.includes("/")) { + const components = identifier.split("/"); + if (components.length !== 2 || components[1] !== "route") return null; + identifier = components[0]; + } + // Bracket escapes remain opaque, so a literal dot cannot create nesting. + if (!/^(?:\[[^\[\]]*\]|[^\[\]])+$/u.test(identifier)) return null; + let parts = identifier.match(/(?:\[[^\[\]]*\]|[^./])+/gu) ?? []; + if (framework === "tanstack-router") { + if (parts.some((part) => part.startsWith("-")) || parts.at(-1) === "lazy") return null; + parts = parts.filter((part) => !/^\(.*\)$/u.test(part)); + if (parts.at(-1) === "route") parts.pop(); + if (parts.length === 1 && parts[0] === "__root") return { root, parts: [], parent: true, layout: false }; + } + if (!parts.length) return null; + const index = framework === "tanstack-router" ? "index" : "_index"; + return { root, parts, parent: parts.at(-1) !== index && !parts.at(-1).endsWith("_"), layout: false }; +} + +export function sourceRouteParent(file, candidates, framework) { + if (!["next-app", "react-router", "remix", "tanstack-router"].includes(framework)) return null; + const child = description(file, framework); + if (!child) return null; + const matches = []; + for (const candidate of new Set(candidates)) { + if (candidate.replaceAll("\\", "/") === file.replaceAll("\\", "/")) continue; + const parent = description(candidate, framework); + if (!parent?.parent || parent.root !== child.root) continue; + const strict = !parent.layout || child.parent; + if (parent.parts.length > child.parts.length || (strict && parent.parts.length === child.parts.length)) continue; + if (parent.parts.every((part, index) => part === child.parts[index])) matches.push({ file: candidate, depth: parent.parts.length }); + } + const deepest = matches.reduce((depth, match) => Math.max(depth, match.depth), -1); + const closest = matches.filter((match) => match.depth === deepest); + return closest.length === 1 ? closest[0].file : null; +} diff --git a/scripts/tests/react_route_hierarchy_oracle.test.mjs b/scripts/tests/react_route_hierarchy_oracle.test.mjs new file mode 100644 index 000000000..223d79f1d --- /dev/null +++ b/scripts/tests/react_route_hierarchy_oracle.test.mjs @@ -0,0 +1,43 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { sourceRouteParent } from "../react_route_hierarchy_oracle.mjs"; + +// Expected parentage is written from the framework examples and source-file +// roles. These fixtures do not invoke the production resolver or read a graph. +const cases = [ + ["next-app", ["app/page.tsx", "app/admin/page.tsx", "app/admin/settings/page.tsx"], []], + ["next-app", ["app/layout.tsx", "app/page.tsx", "app/blog/layout.tsx", "app/blog/page.tsx", "app/blog/post/page.tsx", "app/api/route.ts", "app/global-error.tsx"], [ + ["app/layout.tsx", "app/page.tsx"], ["app/layout.tsx", "app/blog/layout.tsx"], ["app/blog/layout.tsx", "app/blog/page.tsx"], ["app/blog/layout.tsx", "app/blog/post/page.tsx"], + ]], + ["next-app", ["app/layout.tsx", "app/layout.js", "app/page.tsx"], []], + ["next-app", ["one/app/layout.tsx", "two/app/page.tsx"], []], + ["next-pages", ["pages/index.tsx", "pages/blog.tsx", "pages/blog/post.tsx"], []], + ["react-router", ["src/routes/home.tsx", "src/routes/tanstack.tsx"], []], + ["react-router", ["app/routes/concerts.tsx", "app/routes/concerts.$city.tsx", "app/routes/concerts._index.tsx", "app/routes/concerts_.mine.tsx"], [ + ["app/routes/concerts.tsx", "app/routes/concerts.$city.tsx"], ["app/routes/concerts.tsx", "app/routes/concerts._index.tsx"], + ]], + ["react-router", ["app/routes/_auth.tsx", "app/routes/_auth.login.tsx", "app/routes/_index.tsx", "app/routes/about.tsx"], [["app/routes/_auth.tsx", "app/routes/_auth.login.tsx"]]], + ["remix", ["app/routes/concerts/route.tsx", "app/routes/concerts.$city.tsx", "app/routes/concerts/helper.tsx"], [["app/routes/concerts/route.tsx", "app/routes/concerts.$city.tsx"]]], + ["remix", ["app/routes/concerts.tsx", "app/routes/concerts/route.tsx", "app/routes/concerts.$city.tsx"], []], + ["react-router", ["app/routes/a.tsx", "app/routes/a.b.tsx", "app/routes/a.b_.c.tsx"], [["app/routes/a.tsx", "app/routes/a.b.tsx"], ["app/routes/a.tsx", "app/routes/a.b_.c.tsx"]]], + ["react-router", ["app/routes/foo[.]bar.tsx", "app/routes/foo[.]bar.child.tsx", "app/routes/foo.tsx"], [["app/routes/foo[.]bar.tsx", "app/routes/foo[.]bar.child.tsx"]]], + ["tanstack-router", ["src/routes/__root.tsx", "src/routes/index.tsx", "src/routes/posts.tsx", "src/routes/posts.index.tsx", "src/routes/posts.$id.tsx", "src/routes/posts_.$id.edit.tsx"], [ + ["src/routes/__root.tsx", "src/routes/index.tsx"], ["src/routes/__root.tsx", "src/routes/posts.tsx"], ["src/routes/posts.tsx", "src/routes/posts.index.tsx"], ["src/routes/posts.tsx", "src/routes/posts.$id.tsx"], ["src/routes/__root.tsx", "src/routes/posts_.$id.edit.tsx"], + ]], + ["tanstack-router", ["src/routes/account/route.tsx", "src/routes/account/overview.tsx", "src/routes/account/index.tsx", "src/routes/account/-helper.tsx", "src/routes/account/route.lazy.tsx"], [ + ["src/routes/account/route.tsx", "src/routes/account/overview.tsx"], ["src/routes/account/route.tsx", "src/routes/account/index.tsx"], + ]], + ["tanstack-router", ["src/routes/index.tsx", "src/routes/about.tsx"], []], +]; + +for (const [index, [framework, files, expected]] of cases.entries()) { + test(`source parent case ${index + 1}: ${framework}`, () => { + for (const candidates of [files, [...files].reverse()]) { + const actual = candidates.flatMap((file) => { + const parent = sourceRouteParent(file, candidates, framework); + return parent ? [[parent, file]] : []; + }); + assert.deepEqual(actual.sort(), [...expected].sort()); + } + }); +} diff --git a/scripts/tests/test_react_frontend_qualification.py b/scripts/tests/test_react_frontend_qualification.py index 0b565f9f3..33019fcb9 100644 --- a/scripts/tests/test_react_frontend_qualification.py +++ b/scripts/tests/test_react_frontend_qualification.py @@ -19,6 +19,19 @@ class ReactFrontendQualificationTests(unittest.TestCase): + def test_fixture_hierarchy_requires_layout_parents_and_rejects_page_parents(self) -> None: + files = ["src/app/layout.tsx", "src/app/page.tsx", "src/app/admin/layout.tsx", "src/app/admin/page.tsx", "src/app/admin/settings/page.tsx"] + nodes = [{"id": name, "kind": "route", "framework": "next", "source": {"file": name}} for name in files] + pairs = [(0, 1), (0, 2), (2, 3), (2, 4)] + edges = [{"source": files[a], "target": files[b]} for a, b in pairs] + MODULE.assert_fixture_route_hierarchy(nodes, edges) + with self.assertRaisesRegex(SystemExit, "source layout roles"): + MODULE.assert_fixture_route_hierarchy(nodes, edges[:-1]) + with self.assertRaisesRegex(SystemExit, "source layout roles"): + MODULE.assert_fixture_route_hierarchy(nodes, edges + [{"source": files[3], "target": files[4]}]) + with self.assertRaisesRegex(SystemExit, "source layout roles"): + MODULE.assert_fixture_route_hierarchy(nodes, edges + [edges[0]]) + def test_capability_matching_preserves_duplicate_occurrences(self) -> None: graph = { "nodes": [ @@ -155,11 +168,14 @@ def test_expectation_policy_is_manifest_bound_and_reviewed(self) -> None: manifest_path = PINNED.ROOT / "tests/qualification/react-frontend-repositories.toml" manifest = PINNED.load_manifest(manifest_path) policy_path = PINNED.ROOT / manifest["expectationPolicy"] - policy = PINNED.load_expectation_policy( - policy_path, - manifest, - PINNED.digest_file(manifest_path), - ) + # Historical hierarchy rows are deliberately invalidated after source + # review. They must block pinned qualification until re-reviewed. + with self.assertRaisesRegex(PINNED.QualificationError, "record is not reviewed"): + PINNED.load_expectation_policy(policy_path, manifest, PINNED.digest_file(manifest_path)) + policy = json.loads(policy_path.read_text(encoding="utf-8")) + invalidated = [record for repository in policy["repositories"] for record in repository["capabilities"].values() if record["reviewStatus"] == "invalidated"] + self.assertEqual(len(invalidated), 3) + self.assertTrue(all(record.get("invalidationReason") for record in invalidated)) self.assertEqual(policy["schema"], PINNED.EXPECTATION_POLICY_SCHEMA) self.assertEqual( {item["id"] for item in policy["repositories"]}, diff --git a/tests/qualification/code-graph-v1-semantic.json b/tests/qualification/code-graph-v1-semantic.json index 2e39e9bdd..5741e826e 100644 --- a/tests/qualification/code-graph-v1-semantic.json +++ b/tests/qualification/code-graph-v1-semantic.json @@ -1847,6 +1847,66 @@ "sourceFile": "fixtures/code-graph/routes/typescript/vue-router.ts", "targetFile": "fixtures/code-graph/routes/typescript/vue-router-qualified.ts", "reason": "Both modules independently call createRouter. Neither route array declares the other route as a child or imports the other router." + }, + { + "id": "route-containment-independent-9", + "sourceFile": "fixtures/code-graph/frontend-react/src/routes/home.tsx", + "targetFile": "fixtures/code-graph/frontend-react/src/routes/tanstack.tsx", + "reason": "These flat modules declare separate React Router and TanStack routes. Neither declares the other as a child." + }, + { + "id": "route-containment-independent-10", + "sourceFile": "fixtures/code-graph/frontend-react/src/app/page.tsx", + "targetFile": "fixtures/code-graph/frontend-react/src/app/admin/page.tsx", + "reason": "Next page modules are leaves. An ancestor page is not the layout wrapping the nested page." + }, + { + "id": "route-containment-independent-11", + "sourceFile": "fixtures/code-graph/frontend-react/src/app/admin/page.tsx", + "targetFile": "fixtures/code-graph/frontend-react/src/app/admin/settings/page.tsx", + "reason": "Next nesting is established by layout modules, not an ancestor page module." + }, + { + "id": "route-containment-independent-12", + "sourceFile": "fixtures/code-graph/routes/typescript/nuxt/server/api/users.get.ts", + "targetFile": "fixtures/code-graph/routes/typescript/nuxt/server/api/users/[id].post.ts", + "reason": "Independent Nitro HTTP handlers share a URL prefix but do not form a page or layout hierarchy." + }, + { + "id": "route-containment-independent-13", + "sourceFile": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "targetFile": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "reason": "A SvelteKit page and its colocated HTTP server endpoint are separate entry points; the page is not the endpoint parent." + }, + { + "id": "route-containment-independent-14", + "sourceFile": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+server.ts", + "targetFile": "fixtures/code-graph/routes/typescript/sveltekit/src/routes/users/[id]/+page.svelte", + "reason": "A colocated HTTP server endpoint is not a SvelteKit layout and cannot contain the page." + }, + { + "id": "route-containment-independent-15", + "sourceFile": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "targetFile": "fixtures/code-graph/routes/typescript/astro/src/pages/api/items/[id].ts", + "reason": "The About page only renders an h1. Astro layouts require explicit component composition; directory proximity establishes no parent for these independent routes." + }, + { + "id": "route-containment-independent-16", + "sourceFile": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "targetFile": "fixtures/code-graph/routes/typescript/astro/src/pages/users/[id].ts", + "reason": "The About page only renders an h1. Astro layouts require explicit component composition; directory proximity establishes no parent for these independent routes." + }, + { + "id": "route-containment-independent-17", + "sourceFile": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "targetFile": "fixtures/code-graph/routes/typescript/astro/src/pages/blog/[slug].astro", + "reason": "The About page only renders an h1. Astro layouts require explicit component composition; directory proximity establishes no parent for these independent routes." + }, + { + "id": "route-containment-independent-18", + "sourceFile": "fixtures/code-graph/routes/typescript/astro/src/pages/about.astro", + "targetFile": "fixtures/code-graph/routes/typescript/astro/src/pages/files/[...rest].astro", + "reason": "The About page only renders an h1. Astro layouts require explicit component composition; directory proximity establishes no parent for these independent routes." } ] } diff --git a/tests/qualification/code-graph-v1-topology.json b/tests/qualification/code-graph-v1-topology.json index fd6efb4a8..b13b52b10 100644 --- a/tests/qualification/code-graph-v1-topology.json +++ b/tests/qualification/code-graph-v1-topology.json @@ -2,36 +2,36 @@ "schema": "compass.code-graph-topology-policy/1", "topology": { "minimums": { - "communities": 232, - "edges": 1265, + "communities": 239, + "edges": 1277, "exactCrossCommunityEdges": 3, - "exactCrossFileEdges": 62, - "exactCrossFileEdgesPerThousandNodes": 48, - "exactEdgeBearingNodePermille": 709, - "exactEdgeBearingNodes": 905, - "exactEdges": 946, + "exactCrossFileEdges": 56, + "exactCrossFileEdgesPerThousandNodes": 43, + "exactEdgeBearingNodePermille": 710, + "exactEdgeBearingNodes": 918, + "exactEdges": 956, "exactLargestComponentNodes": 54, - "exactUniqueTypedEndpointPairs": 938, - "exactUniqueTypedEndpointPairsPerThousandNodes": 735, - "nodes": 1276, - "uniqueTypedEndpointPairs": 1243 + "exactUniqueTypedEndpointPairs": 948, + "exactUniqueTypedEndpointPairsPerThousandNodes": 733, + "nodes": 1292, + "uniqueTypedEndpointPairs": 1255 }, "maximums": { - "communities": 233, - "connectedComponents": 230, - "exactConnectedComponents": 504, - "exactIsolatedNodes": 371, + "communities": 240, + "connectedComponents": 237, + "exactConnectedComponents": 513, + "exactIsolatedNodes": 374, "exactSelfLoops": 0, - "isolatedNodes": 99, + "isolatedNodes": 100, "selfLoops": 0, - "singletonCommunities": 99 + "singletonCommunities": 100 }, "relationshipMinimums": { "calls": { "exactUniqueEndpointPairs": 13 }, "contains": { - "exactCrossFileEdges": 12, + "exactCrossFileEdges": 4, "exactUniqueEndpointPairs": 606 }, "documents": { @@ -39,8 +39,8 @@ "exactUniqueEndpointPairs": 3 }, "imports": { - "exactCrossFileEdges": 12, - "exactUniqueEndpointPairs": 29 + "exactCrossFileEdges": 14, + "exactUniqueEndpointPairs": 31 }, "maps_to": { "exactCrossFileEdges": 10, @@ -48,7 +48,7 @@ }, "references": { "exactCrossFileEdges": 7, - "exactUniqueEndpointPairs": 49 + "exactUniqueEndpointPairs": 51 }, "renders": { "exactCrossFileEdges": 4, @@ -56,7 +56,7 @@ }, "routes_to": { "exactCrossFileEdges": 14, - "exactUniqueEndpointPairs": 111 + "exactUniqueEndpointPairs": 113 } } } diff --git a/tests/qualification/react-frontend-expectation-policy.json b/tests/qualification/react-frontend-expectation-policy.json index 8619413c7..cdc3bb433 100644 --- a/tests/qualification/react-frontend-expectation-policy.json +++ b/tests/qualification/react-frontend-expectation-policy.json @@ -101,7 +101,7 @@ "recordDigest": "a87ae41e5acfff91e08bd80515e6fcfdf4f1766bc182dd91d8d797f5cedb609e" }, "next.app.hierarchy": { - "reviewStatus": "reviewed", + "reviewStatus": "invalidated", "recordCount": 4548, "counts": { "exact": 4548, @@ -109,7 +109,8 @@ "ambiguous": 0, "unsupported": 0 }, - "recordDigest": "0f7785a4937bd933b8b6ac802bf820280d474ec799fe499c543862789bb92d5a" + "recordDigest": "0f7785a4937bd933b8b6ac802bf820280d474ec799fe499c543862789bb92d5a", + "invalidationReason": "2026-09-26 source audit found that the production resolver and source oracle both selected the first nearby file as a parent. These historical counts/digests are retained, but cannot establish framework nesting. Re-review against documented semantic parent rules before restoring reviewed status." }, "react.render.jsx": { "reviewStatus": "reviewed", @@ -222,7 +223,7 @@ "recordDigest": "4b3dbc677902c1e9339a25b1067dd6b42226f0f1a568dacbefe899c4132f8d7d" }, "react-router.hierarchy": { - "reviewStatus": "reviewed", + "reviewStatus": "invalidated", "recordCount": 175, "counts": { "exact": 175, @@ -230,7 +231,8 @@ "ambiguous": 0, "unsupported": 0 }, - "recordDigest": "0e97550a986d056027a82ec323ef168f5b47ee7ccf22c6a236bd790f62c28324" + "recordDigest": "0e97550a986d056027a82ec323ef168f5b47ee7ccf22c6a236bd790f62c28324", + "invalidationReason": "2026-09-26 source audit found that the production resolver and source oracle both selected the first nearby file as a parent. These historical counts/digests are retained, but cannot establish framework nesting. Re-review against documented semantic parent rules before restoring reviewed status." }, "react.render.jsx": { "reviewStatus": "reviewed", @@ -310,7 +312,7 @@ "recordDigest": "c597d2c919d2d9ba8d4ffc44bf5f3a8fe5ae38b5c1d39f4c2e907534d7f877a9" }, "tanstack.route.hierarchy": { - "reviewStatus": "reviewed", + "reviewStatus": "invalidated", "recordCount": 224, "counts": { "exact": 224, @@ -318,7 +320,8 @@ "ambiguous": 0, "unsupported": 0 }, - "recordDigest": "75311e05f35af3346d28f8f36651bb8f1bb72345c87a02518b429f3dc8554344" + "recordDigest": "75311e05f35af3346d28f8f36651bb8f1bb72345c87a02518b429f3dc8554344", + "invalidationReason": "2026-09-26 source audit found that the production resolver and source oracle both selected the first nearby file as a parent. These historical counts/digests are retained, but cannot establish framework nesting. Re-review against documented semantic parent rules before restoring reviewed status." }, "tanstack.loader": { "reviewStatus": "reviewed", From bb1451a5d9a80d12a8b23fe46ac138b8b4714840 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 20:33:29 -0700 Subject: [PATCH 30/97] docs: record passing route-parent fixture qualification --- .../semantic_route_parent_development_review.json | 5 +++-- docs/design/code-graph-v1-qualification.md | 3 ++- .../code-graph-intelligence-audit-2026-09-26.md | 9 +++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/benchmarks/agent_query/semantic_route_parent_development_review.json b/benchmarks/agent_query/semantic_route_parent_development_review.json index c822d509e..a2c3db924 100644 --- a/benchmarks/agent_query/semantic_route_parent_development_review.json +++ b/benchmarks/agent_query/semantic_route_parent_development_review.json @@ -799,7 +799,8 @@ "semantic-route-parent-benchmark-tests-01.log": "c8bd418cb4120ed7a029b186d3ff1501dd56ff23ac1a065dfd27082b44903e4a", "semantic-route-parent-boundary-01.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "semantic-route-parent-frontend-debug/result-02.json": "5333e65df4f3282f715aeb1e9dd0ff21c8013497e20e988eb84f7a5131f75c68", - "semantic-route-parent-qualification-01.log": "c7d420bc8df5baecb5f9e0898d9833621c8cd80e05493221a157c39f2ea0b54b" + "semantic-route-parent-qualification-01.log": "c7d420bc8df5baecb5f9e0898d9833621c8cd80e05493221a157c39f2ea0b54b", + "semantic-route-parent-qualification-02.log": "648dbf055cc6f5bdf73c8a92a7c7d1795b5c5479e98a94fb77ea5089912f39e9" }, "verification": { "native": { @@ -812,7 +813,7 @@ "sourceOracleCases": 15, "benchmarkTests": 97, "frontendDebugCheck": "Passed source-oracle and explicit layout-pair checks on positive/negative fixtures; no repeat-build or release equivalence claimed by this standalone check.", - "fullQualification": "First attempt stopped at missing default parser bundle. Second attempt is live session 64446 with explicit existing per-worktree bundle; log semantic-route-parent-qualification-02.log. No full pass claimed.", + "fullQualification": "Passed: semantic-route-parent-qualification-02.log, session 64446 exit 0. Includes native scale, deterministic lifecycle, source semantic/topology/Markdown assertions and release frontend matrix/positive/negative/repeat-build checks. Qualifies production commit c20db15b, before the Java constructor-receiver change.", "pinnedHierarchy": "Three historical hierarchy scorecards invalidated; independent source re-review remains required." } } diff --git a/docs/design/code-graph-v1-qualification.md b/docs/design/code-graph-v1-qualification.md index 6e9067a00..36fab6f7a 100644 --- a/docs/design/code-graph-v1-qualification.md +++ b/docs/design/code-graph-v1-qualification.md @@ -56,7 +56,8 @@ set by the frontend fixture gate. The [follow-up review](../../benchmarks/agent_query/semantic_route_parent_fixture_review.json) records both identical-input comparisons and the fixture-only delta, along with mutation checks and topology bound adjustments preserving their prior margins. -These focused checks pass; the complete fixture gate is pending. Historical +The complete fixture gate passed for commit `c20db15b`, including the release +frontend checks. Historical pinned frontend hierarchy scorecards are invalidated until their sources are re-reviewed, so their prior counts cannot establish current qualification. diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 05417091c..c6f8953a0 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1501,8 +1501,10 @@ All **88 script tests**, **15 JavaScript oracle cases**, and **97 benchmark tests** pass, as do formatting and the product-boundary check. The first full fixture invocation stopped because its default parser-source path was absent. A second invocation uses the existing parser bundle in this worktree's target -directory; its complete result remains pending. No full qualification pass is -claimed at this checkpoint. +directory and completed with exit 0. It passed the scale, semantic, topology, +Markdown, lifecycle determinism, and release frontend qualification stages. +This full pass qualifies production commit `c20db15b`; the pinned hierarchy +scorecards remain invalidated and require separate source review. #### Java constructor receiver diagnostic @@ -1520,8 +1522,7 @@ The native evidence and corrected graph inspection agree on the missing calls. ## Next evidence to collect -1. Finish the full qualification run for framework-specific route parents and - re-review the invalidated pinned hierarchy scorecards from their sources. +1. Re-review the invalidated pinned hierarchy scorecards from their sources. The corrected fixture and selected real-source evidence above do not replace those broader checks. 2. Extend source-proven constructor, loop/result/iterator inference to recover From 22814e5901f4fa3530b580bed70d175f1fb34a60 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 20:54:19 -0700 Subject: [PATCH 31/97] fix: resolve Java constructor method receivers from syntax evidence --- CHANGELOG.md | 6 + COMPATIBILITY.md | 12 + MIGRATION.md | 6 + .../java_constructor_development_review.json | 1182 +++++++++++++++++ crates/compass-files/src/cache.rs | 2 +- .../compass-languages/src/evidence/build.rs | 84 +- .../tests/java_constructor_receivers.rs | 248 ++++ ...ode-graph-intelligence-audit-2026-09-26.md | 75 +- docs/reference/universal-semantic-evidence.md | 13 + 9 files changed, 1600 insertions(+), 28 deletions(-) create mode 100644 benchmarks/agent_query/java_constructor_development_review.json create mode 100644 crates/compass-resolve/tests/java_constructor_receivers.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2027e8fc4..f96ff9a87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Resolve Java method receivers constructed directly at the call site, + including qualified types and bounded parentheses, while retaining overload + and occurrence evidence. Stop turning arbitrary receiver expressions into + invented external type names. Enclosing-instance construction no longer + selects an unrelated imported class. Rebuild older AST caches. + - Resolve file-route parents from framework nesting conventions instead of choosing a nearby module. Preserve layout, flat-route, index, pathless, and non-nesting distinctions; retain ambiguous parents without inventing edges. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 352e92b54..fa6ecf880 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -219,6 +219,18 @@ from 3 to 4, invalidating older disposable AST facts across languages. Producer capabilities and evidence/graph schemas are unchanged; published history remains immutable. +Java method receivers now use constructor syntax evidence for direct named +object creation, including qualified type names and bounded parentheses. +Overload selection still requires the existing argument evidence. Anonymous +subclasses, explicit enclosing-instance creation, arrays, casts, and chained +results retain unresolved method candidates when their receiver ownership is +not proven. Arbitrary expression text no longer becomes an external type name. +An enclosing-instance construction also retains its receiver qualifier instead +of selecting a same-named imported class. AST cache semantics advance from 4 +to 5, rebuilding older disposable facts across languages and invalidating old +build seals. Producer capabilities, graph/evidence schemas, and published +historical realizations are unchanged. + ### Framework route hierarchy Framework route hierarchy now requires a recognized filesystem-convention fact diff --git a/MIGRATION.md b/MIGRATION.md index 90fb5e6eb..3247673a8 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,12 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Rebuild Java graphs to receive the constructor-receiver correction. Direct +constructor method calls can gain source-proven targets; invented external +targets and unrelated imported-class construction edges can disappear. Normal +builds discard AST cache versions older than 5. This does not rewrite historical +graphs or change the graph schema. + Rebuild graphs with programmatic framework routes to remove filesystem-derived containment between independent routers and unsupported file-route parents. Framework-pack semantics version 8 diff --git a/benchmarks/agent_query/java_constructor_development_review.json b/benchmarks/agent_query/java_constructor_development_review.json new file mode 100644 index 000000000..5acc786fd --- /dev/null +++ b/benchmarks/agent_query/java_constructor_development_review.json @@ -0,0 +1,1182 @@ +{ + "schema": "compass.java-constructor-development-review/1", + "scope": "Repeated five-language development comparison. These repositories/questions have already informed changes; neither fresh held-out evidence nor representative precision.", + "runId": "java-constructor-panel-a-01", + "suiteDigest": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "runnerDigest": "173242988699eb6fabd175c293634d3cf37c1394f76429540fd7af2a48ef7026", + "tools": [ + { + "binary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/java-constructor-provenance/compass", + "binarySha256": "c2de3e3ae7b0a12ac14ef24757d63bc95f199a7e1b96b639f0cfa8f18e02422f", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30" + }, + { + "binary": "/Users/haipingfu/.local/bin/graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67" + } + ], + "productionSourceSha256": { + "crates/compass-files/src/cache.rs": "1526ae8a7d98feb92333464209cce81d5a720faee714483ffa4fbc63bde1e6ee", + "crates/compass-languages/src/evidence/build.rs": "70a4b9b648aa6dcedd5ebdcfc4abb4be024f2347b2f58e91f969d734dc1bbd49" + }, + "regressionSourceSha256": "1fd1b3a77f5ae2d1c7bf34f6d94e0aa513811c177c4452ef1e896f7102722a08", + "metrics": { + "compass": { + "text": { + "medianAnswerTokens": 148.0, + "medianFirstPageTokens": 148.0, + "medianWallMs": 919.0, + "passRate": 0.8364, + "passed": 46, + "questions": 55 + }, + "selectedSourceRelationships": { + "passed": 17, + "allOccurrencesPassed": 17, + "total": 21 + }, + "sourcePaths": { + "passed": 6, + "total": 10 + } + }, + "graphify": { + "text": { + "medianAnswerTokens": 104.0, + "medianFirstPageTokens": 75.0, + "medianWallMs": 162.0, + "passRate": 0.8364, + "passed": 46, + "questions": 55 + }, + "selectedSourceRelationships": { + "passed": 20, + "allOccurrencesPassed": 17, + "total": 21 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + } + } + }, + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "d69152cc8900afc3f11756b8ce841adbed98ae6663fee4ebc64957974c8f860c", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "panelDelta": { + "comparisonRun": "semantic-route-parent-panel-a-01", + "byteIdenticalCompassGraphs": [ + "chi", + "click", + "redux", + "walkdir" + ], + "jsoup": { + "beforeGraphSha256": "8035487618e4e96af8b7cc668d92eaea9ee3ec2a831c486d20055bc808fa23f6", + "afterGraphSha256": "d69152cc8900afc3f11756b8ce841adbed98ae6663fee4ebc64957974c8f860c", + "nodesBefore": 6116, + "nodesAfter": 6116, + "edgesBefore": 20973, + "edgesAfter": 21095, + "addedCalls": 122, + "removedEdges": 0, + "changedCommonEdges": 0, + "changedNodesExceptCommunity": 0, + "productionOccurrences": 10, + "testOccurrences": 112, + "staticSourceReview": "All 122 added occurrences reviewed against receiver expressions, package/import context, target declarations, overload argument types, exact byte anchors, and source-to-target direction. This is a complete review of this delta, not an unbiased precision sample or runtime call coverage.", + "sourceFileSha256": { + "src/main/java/org/jsoup/Jsoup.java": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7", + "src/main/java/org/jsoup/helper/HttpConnection.java": "9f439dc7f7aa03d18a1fbff716cfb562f9e2667b249a60dfc329f5232b3c1145", + "src/main/java/org/jsoup/helper/W3CDom.java": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725", + "src/main/java/org/jsoup/nodes/NodeUtils.java": "04dffffb32b1fa4bfeb237205faa6e37433cf773ecc57a9e89de3f9d1eb67bd9", + "src/main/java/org/jsoup/parser/TagSet.java": "26261d2c9ee3d2e978c184f15328022488af3264ae8b1c54b6bd8fcdd02a0dff", + "src/main/java/org/jsoup/safety/Safelist.java": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501", + "src/test/java/org/jsoup/helper/HttpConnectionTest.java": "62d806a0f78b538163554bd09e4cd9cdd58f5a5dbfcaf1addf600dd73a357355", + "src/test/java/org/jsoup/helper/W3CDomTest.java": "3def0f732425cf7a1808e2957363a41bdd55c85f56b54708874663911302d989", + "src/test/java/org/jsoup/internal/NamespaceBindingsTest.java": "b23cb0e3d78202179f166e4c6c14a13dc076ce2dd278d24b64edad7757b9fd28", + "src/test/java/org/jsoup/nodes/AttributesTest.java": "a92b281635b30dadee55ab7f7bd0aa04977209bb3209e7084607afbd08d44391", + "src/test/java/org/jsoup/nodes/ElementTest.java": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc", + "src/test/java/org/jsoup/nodes/NodeIteratorTest.java": "149503af2e01faa6ea85c170da08435790458e603aaadb4bea15c93dc37e7681", + "src/test/java/org/jsoup/parser/HtmlTextHandlingTest.java": "ffd7a47666c8a822d56083ce9c6516f22c6414950a5e9e586b2f52cfb72b36fa", + "src/test/java/org/jsoup/parser/HtmlTreeBuilderTest.java": "d7bb5ef59792baa58e3455be6e28c90e81e0469ed33fec5746815c6aef505a2a", + "src/test/java/org/jsoup/parser/NameNormalizationTest.java": "bb07fcb09a43a595991b79d0ec46aabae04b2a27bb3c09101b5be8382d122429", + "src/test/java/org/jsoup/parser/StreamParserTest.java": "83588e6ebafd1c3f390e7deefcd0c00e3b9d48366d41680778a2c2a12e512677", + "src/test/java/org/jsoup/parser/TagTest.java": "c9eb06ac8011a92c4ed556a7fa82e6a66463d6be233e6fe8fad819bf66055026", + "src/test/java/org/jsoup/safety/CleanerTest.java": "8f4e9abb7574ab5552634f21f025e2c33c3df95d4622f3f05324c32734425bfc", + "src/test/java/org/jsoup/safety/SafelistTest.java": "af3b581643e773f14bc71c83abfbff499adf706c3d9f89e919a997c244329c4f", + "src/test/java/org/jsoup/select/ElementsTest.java": "31900b1bd82389ef2ad069c3b85a79145106cdc7e52fd9a6d1d63db001e4eb08", + "src/test/java/org/jsoup/select/SelectorTest.java": "0560f38b89cb82e8f7e12386d468b43bd069874941be5fa85ad3227363e18d66" + }, + "targetGroups": [ + { + "targetId": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "target": "org.jsoup.safety.Cleaner::isValidBodyHtml", + "graphSignature": "isValidBodyHtml(String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "sha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 435, + "edgeId": "sha256:38d6c354f2e4a136528e0f8d4261fc07f6a2c9e18886865384966819efd9e046" + } + ] + }, + { + "targetId": "sha256:3e848e37db6f32068eecbb3958e87a0a8e273408287bd872db4c47afc8b142ff", + "target": "org.jsoup.helper.UrlBuilder::build", + "graphSignature": "build()", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/UrlBuilder.java", + "line": 33, + "sha256": "874ee79c27016735ff8a5141e1f09f76be40438e1f9bec99311b9a34a6e6f593" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/helper/HttpConnection.java", + "line": 451, + "edgeId": "sha256:c307893c6c762335b0dfe97bf7fd1bb0793e541bca05915361694c892bd3298d" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 320, + "edgeId": "sha256:211bc74793ff58e4c3e6d57c96890cc992dd886fa3b60e63d70393b6bc652307" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 326, + "edgeId": "sha256:22b88548293ed5c0fc25210fe78c12f4507294dea3445d7c88dc35e20afb9e3d" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 332, + "edgeId": "sha256:6efb0e22cac18b5a087a24a78af6284b81a99033e62ba4eaf8382256f10d0847" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 333, + "edgeId": "sha256:03799930f56f4189db68c10ddd5229b2d3cbdb1f61aad7c89360f920dddf90c8" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 340, + "edgeId": "sha256:c02290532de01e39d64548000667f53312643dd9023dddfc0ff1bbae07826538" + }, + { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "line": 341, + "edgeId": "sha256:e771a487eeb791ffbbedf9d9360a51820d057fb4fba2dc2d6502ab022be71e41" + } + ] + }, + { + "targetId": "sha256:803e6b1d2452eb819552278e067733e662e4da423240c9ccece097e43ad41c4e", + "target": "org.jsoup.helper.HttpConnection::KeyVal::inputStream", + "graphSignature": "inputStream(InputStream)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/HttpConnection.java", + "line": 1439, + "sha256": "9f439dc7f7aa03d18a1fbff716cfb562f9e2667b249a60dfc329f5232b3c1145" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/helper/HttpConnection.java", + "line": 1405, + "edgeId": "sha256:91546594788d3055205782a62af9886f1e4e41c67ac4ce4fa2e0882eb67f0742" + } + ] + }, + { + "targetId": "sha256:eabdf2b85e2767153036daf99328b201e6da9977b01e4c7966e04e7f65958609", + "target": "org.jsoup.helper.W3CDom::fromJsoup", + "graphSignature": "fromJsoup(org.jsoup.nodes.Document)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "line": 187, + "sha256": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "line": 104, + "edgeId": "sha256:7658b11fc681e412ec9d0b0ab53fb5cd132dea77fa84312667a9f7c3d7601c00" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 157, + "edgeId": "sha256:eab007225198fba0bd108747e2f17a8da32b3f02abdf076892e3b23a286c4e6e" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 179, + "edgeId": "sha256:0b6af4a200b1f195f88adbbfbffbb5853de7bb19b7a2597caf0c8951eb565315" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 303, + "edgeId": "sha256:295da2a04c113b4b9e40d70e6316a7aec91197bf81c538902ce45594dc74cc4b" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 477, + "edgeId": "sha256:a08c78612e65a61069016b10ee7f9effbdbebfee45724ad6eea52b62852dc42c" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 615, + "edgeId": "sha256:a1bb0d3d5b740a1e80cfcb29fbc9ca3537d21b27329bf73dad037f13c29f6281" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 709, + "edgeId": "sha256:8da9adae5a529e5683c983b0a3b43e3ba52d2052eb38aa5fc9a713f4eaa766dd" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 727, + "edgeId": "sha256:169ae9ed688813179fbe467dedf42d2946b78ccd15e92cfa131f3a7ba1ac7138" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 745, + "edgeId": "sha256:819ab6171dbebf1cf63d80cd33cdd258c793a93b27109df76c4b3ccafbfbddfd" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 760, + "edgeId": "sha256:818842bbffa4daa16110f6aab76f7865bec8a91fe27898f52f93f61195478f0e" + } + ] + }, + { + "targetId": "sha256:4557280905e92abc42184d763fdd3e7a8355804c908e6cca2c861302affff47b", + "target": "org.jsoup.nodes.Document::outputSettings", + "graphSignature": "outputSettings()", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Document.java", + "line": 535, + "sha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/nodes/NodeUtils.java", + "line": 27, + "edgeId": "sha256:d0e6b2e002f740de2475f737a90f907d0ed8dce13b7bfcf7881af43e74f8a66d" + } + ] + }, + { + "targetId": "sha256:8503365ca5c06c4e01990db283d7f7a0efe276cd602c8569aeabf17dbb83e8e9", + "target": "org.jsoup.helper.W3CDom::namespaceAware", + "graphSignature": "namespaceAware(boolean)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "line": 91, + "sha256": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/nodes/NodeUtils.java", + "line": 49, + "edgeId": "sha256:c3b5fde74569579fdb5d8575f0300778ed8777fecdd6ca0c40173c35668d0df0" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 553, + "edgeId": "sha256:0cc78b3aa81570f43990747c6b2e727f79ed7b5b438096663ea09ebaadf1b1b8" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 566, + "edgeId": "sha256:a237cbca84c7e1a501d5eca4f275386f9ee30cb0503573252936a707116d5987" + }, + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 656, + "edgeId": "sha256:c37803f291a28171ddcf507a9650ae53eff99417e308471eee7c5a433ccf0299" + } + ] + }, + { + "targetId": "sha256:8a167c33bee793ed8684dae1261e55159066ea474e755ffb10273e8e93f789ee", + "target": "org.jsoup.parser.TagSet::setupTags", + "graphSignature": "setupTags(String,String[],Consumer)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/TagSet.java", + "line": 301, + "sha256": "26261d2c9ee3d2e978c184f15328022488af3264ae8b1c54b6bd8fcdd02a0dff" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/parser/TagSet.java", + "line": 284, + "edgeId": "sha256:ad84fc261ef9822f0a777f7fb1745f4885ae19384af75e2f9fa3b14ef6260d48" + } + ] + }, + { + "targetId": "sha256:dd1ac0cfe47b9da2be7b8e1805783ac9a9ab19e1356d821cb8ca024a4a8abb07", + "target": "org.jsoup.safety.Safelist::addTags", + "graphSignature": "addTags()", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 254, + "sha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 115, + "edgeId": "sha256:59d63e0c68a5d32af6bebc86da0302d280336bb9f1234ce60c01a3dfe26ea8e3" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 136, + "edgeId": "sha256:9de30628de07ab75145f0cb02884eec179fe69ad5d2a31e6a26e469219bb9680" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 180, + "edgeId": "sha256:202fcdd6300a2c37b1326f7c85148e102be607e3b9c7aeb89643244903bcddda" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 309, + "edgeId": "sha256:ade3f925b021192773dc6b7f466a075711488ecbafca0152683d1176e99692fc" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 319, + "edgeId": "sha256:f3ab4e4bcc532b6b22a1c4ce47805371ccc552c1ad56b7294332ed17ac08d442" + } + ] + }, + { + "targetId": "sha256:e44f87b4c26d2d629d34105231dbfb7bade0144e66fb052ab7d0f4f96e61a849", + "target": "org.jsoup.helper.W3CDom::convert", + "graphSignature": "convert(org.jsoup.nodes.Element,Document)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "line": 242, + "sha256": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 642, + "edgeId": "sha256:f4b9b33104469a14aec9eabf7c7387701a430e7bfc25f4140bcf48385535c8dc" + } + ] + }, + { + "targetId": "sha256:b74a4e8c8955717abc8c9572d049357040793a246cdd93434d49aa71b9d403ae", + "target": "org.jsoup.helper.W3CDom::asString", + "graphSignature": "asString(Document)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "line": 335, + "sha256": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/helper/W3CDomTest.java", + "line": 647, + "edgeId": "sha256:676a951562396f7fa933de79b573bdf82b10039579a58bf850766b61bc5eea78" + } + ] + }, + { + "targetId": "sha256:fc7e5911b18d64599b5988e0f5031218d5440d0f8389d6e1bb07d903da205d4c", + "target": "org.jsoup.nodes.Attributes::put", + "graphSignature": "put(String,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "line": 189, + "sha256": "39551b1d008f2a77b212b5b16e09e257b0ec9d8f118a0b7131a1cfe11ac32875" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/internal/NamespaceBindingsTest.java", + "line": 19, + "edgeId": "sha256:72c39e37aa7abecd130988d1a516fd1a653ca16baddec7cd58c13e78afa4ff46" + }, + { + "file": "src/test/java/org/jsoup/internal/NamespaceBindingsTest.java", + "line": 23, + "edgeId": "sha256:342ff847a3194fc7f0018239e61eb5b23e8533ae8c489713bc956b1045b2530b" + }, + { + "file": "src/test/java/org/jsoup/safety/SafelistTest.java", + "line": 66, + "edgeId": "sha256:90dab3ce76ef9741048d7c6c71f49005b4d7482b6871e0ffabbbbb9113ff16ae" + } + ] + }, + { + "targetId": "sha256:b6e98a981b2f46c6c2e00cfb2e6eb873e089261fce4da60b0ef8ca90d57f7da4", + "target": "org.jsoup.nodes.Attributes::add", + "graphSignature": "add(String,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "line": 171, + "sha256": "39551b1d008f2a77b212b5b16e09e257b0ec9d8f118a0b7131a1cfe11ac32875" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/nodes/AttributesTest.java", + "line": 350, + "edgeId": "sha256:9f990a1f8fd86180b909f341ac214785b58cb90b48ef9eed4c939bebe4b79a17" + }, + { + "file": "src/test/java/org/jsoup/nodes/AttributesTest.java", + "line": 355, + "edgeId": "sha256:4388a9b379500e1a2060c709c152ac7f6e49b239a25b0d180e12dffc26aa73ce" + }, + { + "file": "src/test/java/org/jsoup/nodes/AttributesTest.java", + "line": 360, + "edgeId": "sha256:4fa2a6c511ca9fffb4bad2ab9a1ae10d724dfa7e8e2c6320f0db09fe754c7ae8" + }, + { + "file": "src/test/java/org/jsoup/nodes/AttributesTest.java", + "line": 365, + "edgeId": "sha256:d9ba883591f424748b543357baddb08fb16fb51742dc76d9c1ab4b4dcc92b36c" + }, + { + "file": "src/test/java/org/jsoup/nodes/AttributesTest.java", + "line": 384, + "edgeId": "sha256:338feb82f43baf2694142b46506caa0d744355567d9e0b98a715cfb80624d96d" + } + ] + }, + { + "targetId": "sha256:36d9c8676ae1a937613f565568e879b9b3fe720f7114d0d040fc5afd95b6f624", + "target": "org.jsoup.nodes.Element::text", + "graphSignature": "text(String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 1715, + "sha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 997, + "edgeId": "sha256:b9357c123b2b4ab1059c27b3e0dd4acde8eb885a30372a7b329b57c9445b0688" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1390, + "edgeId": "sha256:8ded17ed38e3f5dabfb37fe8415cf9ec8a4b0e88b384cb58d595d666ef27d325" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1391, + "edgeId": "sha256:a709d010ec9a4aadba966b43c869bac51a7b05cccfee3d2434d2c2f521298af9" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1860, + "edgeId": "sha256:5d96f25272c6c1e365c3dccb9aea4dda9cd473b1764ed1e6d297e3d72f84a0c6" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1861, + "edgeId": "sha256:f467d635895825d0037be9768a36f3439139ec2e2bbb332c0d152e7ec12e69ef" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2324, + "edgeId": "sha256:caf870e03dce95bccdbe6a3152fe3a7a9902173731333a7a3f8a0ecf5f7c4741" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2325, + "edgeId": "sha256:30be2c1559d683749d838858991d0f93ddb5e0537003dac291108bbb0c4758e3" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2348, + "edgeId": "sha256:dba349b486b4367f18d17d27077b07bec02bfd972582e99db5f2be7a568ccce9" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2349, + "edgeId": "sha256:ff57999c87c85b96609ad19c9a25fc875878f5d312b8e65d16ba830e5e5127fe" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2355, + "edgeId": "sha256:f5b6d215f0289834e0a29cd7a11700aea3e139518684dcd18af5d71672bd8248" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 3399, + "edgeId": "sha256:bdab0e3f92ce6b126d96adddba7a9d4ec4e766277e6a9ebeddfb427bc0ce672d" + }, + { + "file": "src/test/java/org/jsoup/nodes/NodeIteratorTest.java", + "line": 152, + "edgeId": "sha256:7531e7cab4f54a9fafbb7cbe490c03413376852685c9ab63d0cc25479a2f81a8" + }, + { + "file": "src/test/java/org/jsoup/nodes/NodeIteratorTest.java", + "line": 165, + "edgeId": "sha256:62780a8f0bad7e4f9b0b7cf10b1a8fc4713594c5595df15a1a1f3fa83e6d6746" + }, + { + "file": "src/test/java/org/jsoup/select/ElementsTest.java", + "line": 174, + "edgeId": "sha256:2020c8cfb97651772ef792a417e8883a00de8e99b5d519fc5d9c1c9cab8d4c6a" + }, + { + "file": "src/test/java/org/jsoup/select/ElementsTest.java", + "line": 189, + "edgeId": "sha256:985ed65997afd6f3f68e7713e1279f5f8c13e97fcb319bf27bb9adf58fa1e78b" + }, + { + "file": "src/test/java/org/jsoup/select/ElementsTest.java", + "line": 198, + "edgeId": "sha256:e6174edc8761d9139db3c62c15e2576b2707465009888e10bede74a450dbaab3" + }, + { + "file": "src/test/java/org/jsoup/select/ElementsTest.java", + "line": 199, + "edgeId": "sha256:04a38567d231c68e7cc62f9e0dc0a9a5f465b6ea663ee792f54a6776f084e2b0" + }, + { + "file": "src/test/java/org/jsoup/select/SelectorTest.java", + "line": 1139, + "edgeId": "sha256:d6558681deb2cbd0a8272ba406b811d6a299ef500529b6bd5727e4f639ad5f97" + } + ] + }, + { + "targetId": "sha256:73af62fff71eae3c5a4f0926646fb36bf41f6c59df3fc1a93b9a4e5d8ee2433a", + "target": "org.jsoup.nodes.Element::id", + "graphSignature": "id(String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 267, + "sha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1124, + "edgeId": "sha256:f1b0e58068c5c59dd15ba9acc6601602e8d2f714fee5294328edc77dcec6a090" + } + ] + }, + { + "targetId": "sha256:aa100e1e077020db7e859d39158a3b59b02317fdb601f1891e79e53770c9bb90", + "target": "org.jsoup.nodes.Element::attr", + "graphSignature": "attr(String,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 279, + "sha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1574, + "edgeId": "sha256:6fa6789dd67609942b8aca232fed9c80b63e3ecb5670a0e38f2b26e7b8408a37" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1575, + "edgeId": "sha256:5280ff39aa117f5f1c6be1fa9b5413273222ceb205cde4a38c535bd41f2f8765" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2190, + "edgeId": "sha256:a6b5d61c6c4bce57131d122647c272d4dd547f38eab53b8b4a5d6b3dfb3d308c" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 2200, + "edgeId": "sha256:4cbb6aa96e0d464ce7342500f7d4a356d6965446a6cb164e9006fe7e9523a546" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 686, + "edgeId": "sha256:be02dd1e264ab662d738702a3771fa66f3efcd30d0fb18b42de6545fa4f0a95d" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 714, + "edgeId": "sha256:5fa77ecab31944cbe2932ca85c0b6004acf7a6c85b99bf62c5dded19af4f9787" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 757, + "edgeId": "sha256:7a54c9e1c1ce38d4a2cbaaa5410449606c0cbf3f1e3c9332af7de7fbf6b3eceb" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 770, + "edgeId": "sha256:e7ae674d916e5c439f71a8d6de8dee54edc6c99c90cc385e6501a612708d0886" + } + ] + }, + { + "targetId": "sha256:9b3f21ab8799d0098c1a7ca7ab4c6a3c04f1026d9938125b3f2c9d36c0fff677", + "target": "org.jsoup.nodes.Element::appendChild", + "graphSignature": "appendChild(Node)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 813, + "sha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/HtmlTextHandlingTest.java", + "line": 49, + "edgeId": "sha256:e313255c222afafa3060662482607d965731e1f8c22f033ffd07280342ab9ed3" + } + ] + }, + { + "targetId": "sha256:3d7ea7f7d6759755fb2ae6fff43c1865d390d23a825915ec256bf533b5fbf809", + "target": "org.jsoup.parser.CharacterReader::consumeToEnd", + "graphSignature": "consumeToEnd()", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 528, + "sha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/HtmlTextHandlingTest.java", + "line": 98, + "edgeId": "sha256:a243df929ac03cedbb5411fde9831c5e02d549d4f2c3140fcac5bb099429802f" + } + ] + }, + { + "targetId": "sha256:bd00aad5322991cb1b3e2c4fab7c006fdbd92ee355fe93d99d4329b752cfdb8d", + "target": "org.jsoup.parser.Parser::setTrackErrors", + "graphSignature": "setTrackErrors(int)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/Parser.java", + "line": 168, + "sha256": "2b8baa95140fbf12fad9c874b42d8d31e24b186fd7c665c1708395e6d852ba98" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/HtmlTreeBuilderTest.java", + "line": 133, + "edgeId": "sha256:9825327a9e69f1927bc04e954aaad6797110bf91f08bd47333bb508836301e32" + } + ] + }, + { + "targetId": "sha256:eece375f79deef58e8fba0f31ea7d0cdbf939b5f6de77b6b8cf4e191e4fb084f", + "target": "org.jsoup.parser.StreamParser::parseFragment", + "graphSignature": "parseFragment(String,Element,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/StreamParser.java", + "line": 122, + "sha256": "5a50cf810031c426623c68cc630218c3e6725e1519bb039143777f9f19637855" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/HtmlTreeBuilderTest.java", + "line": 193, + "edgeId": "sha256:c588d389b1aa29f9abb1bcec259a23d22c5e678daabada7f7a5bf8fe64b4afb7" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 428, + "edgeId": "sha256:d253790571d6a390798874265f06f1dafc91aa830c0c0d0f9f8f42c0dfdafbf1" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 688, + "edgeId": "sha256:2a86fb403614f22a14b40fefe5cf3dbb7eb87a7bafa366ead3fdcf314c8e2883" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 716, + "edgeId": "sha256:0b8c9ee0f9b12db2fe5b95fc524905d02ffb3b321ace5d338e8c0eb3639baf61" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 744, + "edgeId": "sha256:e2cafe46a0451a1c57ced5880658f11017c0cd8a199e7007b94229ffbe9f350c" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 759, + "edgeId": "sha256:75be11b70e0cc8ad21b9f946cfed7b673f43e1c935fb5fb194745ada9d37a333" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 772, + "edgeId": "sha256:070b8bd73264d9ea54e859af555c177174c2ae16932086adabd93d9039070c8f" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 789, + "edgeId": "sha256:8788de3bc00e3f231011940878ef12d299a8aa15a8cad04d16e9f4bae49a3571" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 810, + "edgeId": "sha256:e36f8f2ad4cb8c2e4bcb8b8e15be382ef18f5796f04308c224a8d49a84975d97" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 840, + "edgeId": "sha256:885eb30ae1ffc5ffe07c7a61b4de5d6763c405573d9d4f3d7a74d94787b577bd" + } + ] + }, + { + "targetId": "sha256:aca06bc408bbbb11866b9230301285def8ea2119582345809e1037289746e512", + "target": "org.jsoup.parser.StreamParser::parse", + "graphSignature": "parse(String,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/StreamParser.java", + "line": 96, + "sha256": "5a50cf810031c426623c68cc630218c3e6725e1519bb039143777f9f19637855" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/HtmlTreeBuilderTest.java", + "line": 287, + "edgeId": "sha256:99eba178a75287ea5a5e959d55f4b8e9bd641268024d2e2fb18ddab69b420240" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 42, + "edgeId": "sha256:4253dfe4636cc4314e22fce8bf3c18c4436e6e84e0e32ead16dacc85f4ef169b" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 54, + "edgeId": "sha256:8892009eb04ec4543004c662d1d857816ed4bf5865d74bb78b97830a93c6c132" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 66, + "edgeId": "sha256:fd963536635cdbe5d2e243f3f265253d06936f7d8e81cfdb910276ba4ffe7746" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 101, + "edgeId": "sha256:38d49a4f02cf42738fa52160dbf4b506db5947157d7c7e384b43bf85e9e466d2" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 117, + "edgeId": "sha256:1ee8819a2e460a3943c380e45bf7c94ba08570e731f9205f420b4d8a1db297cf" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 200, + "edgeId": "sha256:f2933f3d5c304214cbb6188548702ae35acf0de12f8cd4633c6fcd6e94a0ef9f" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 225, + "edgeId": "sha256:63131841eaa69d632971ded65f0bb68aac29d0713650116af4cdb0bfba3d0879" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 256, + "edgeId": "sha256:92782a68438ac00a3369ff21702a9ca27e277d6addc7ad86da7ac74739a81f04" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 270, + "edgeId": "sha256:3f355a2cbe86aa98faf3c5ee70a3c3f97634b79c1ea186f13ac3525abd5719db" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 278, + "edgeId": "sha256:227d186c073bcd5c850d9a8ef086a1d99700992d7d4e62cbd219418bb9a3861a" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 288, + "edgeId": "sha256:58059510b4d9ffa7e7128a6407cb9e9a07e99d2ee34e3632fdaaa1da83be905c" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 299, + "edgeId": "sha256:aa77fe1b304f940619707cdfa17f179d3f6aea0be6e66a544622455ba76bcdb3" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 310, + "edgeId": "sha256:06879e092386a18e818baf4ddd24e72406aa97b283d18ef365f62a6b829c9d48" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 323, + "edgeId": "sha256:9ee33739a02a3401cc7081896b5fdd52a89b681111e6919c9227583f692fa3d8" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 360, + "edgeId": "sha256:cc919422f4e2818356cd77ea8e9448141c787a4d0bf564c920386fc6f13cf52d" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 373, + "edgeId": "sha256:0c7f4dc9d98b131d46e7d301f3f19efe010295012a91f5d5079da2b04f6e4851" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 385, + "edgeId": "sha256:d632fd85a1efe5bf3e9ffbde6ba0b2fc97c60f60393f129e0ba875db3d9f43f1" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 397, + "edgeId": "sha256:0bf4c776a494bb0e0d0cb72f76f0198180509e91a0a32ece63fb13859133241d" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 415, + "edgeId": "sha256:41a2fa4bf181a1f2fbf9f295888919e67fff52eecfdff111c23f0214568b8678" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 466, + "edgeId": "sha256:f376db8110d2a3fab3c7601fa64041a2638ab135e53ed60ccb3624bbad92e59b" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 483, + "edgeId": "sha256:6dc3a987e5696da59bbdabb791dda53eeeff05f75f3ece4301adbe852b29b23f" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 517, + "edgeId": "sha256:ba1487b8ddaa9924849e6ce95aa33757d6824256d0db00cd40430a2b34277a6d" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 534, + "edgeId": "sha256:b83ad0eb132e489833fb7b1f0e7b347fbb9834d1b55504365e0aabfb48fd5ab6" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 589, + "edgeId": "sha256:316532aa320e906cbda33c6482b592de3b207a542375bf2e8545c1dedb7f56e0" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 865, + "edgeId": "sha256:145082ab4f1a07855506597d5c3dd8c9aafbf49b21235ae818306742ac533316" + }, + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 877, + "edgeId": "sha256:138e86a72a46bff127e6e2bf41fbaef2f58f4688210ad151b782454a161d665b" + } + ] + }, + { + "targetId": "sha256:cceba530371a95b96ccb0655acc673f295f0315c86366134b363441684be4d8c", + "target": "org.jsoup.parser.Tag::normalName", + "graphSignature": "normalName()", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/Tag.java", + "line": 141, + "sha256": "98b0a56d5963298da2176eeb555ae1e66cf43ddd0df28a25ebec1fa8ed7bee5a" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/NameNormalizationTest.java", + "line": 62, + "edgeId": "sha256:9cfa7861597d4bfff63490f1c4b070a3e2b57398afe1559b11853173756a23bb" + } + ] + }, + { + "targetId": "sha256:94c097129a2d269e39183f18de003e7ec747b587137da88fe81f810b2a3dc92a", + "target": "org.jsoup.parser.Tag::set", + "graphSignature": "set(int)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/Tag.java", + "line": 173, + "sha256": "98b0a56d5963298da2176eeb555ae1e66cf43ddd0df28a25ebec1fa8ed7bee5a" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/NameNormalizationTest.java", + "line": 116, + "edgeId": "sha256:ef192b2fe8cbb85f42c6c4c59ee317f2bd67bc62124e151543c2dc5232c1351f" + }, + { + "file": "src/test/java/org/jsoup/parser/TagTest.java", + "line": 200, + "edgeId": "sha256:0a264e80d6c41b356cc208871c47dee6814de06a0ec7d69195cbf4aedacee860" + }, + { + "file": "src/test/java/org/jsoup/parser/TagTest.java", + "line": 201, + "edgeId": "sha256:07b1d5cac48ec1436f36ecc59ad71b34caa84c04c7a42955d124e8edee254e15" + }, + { + "file": "src/test/java/org/jsoup/parser/TagTest.java", + "line": 213, + "edgeId": "sha256:9be8348f9788c2f426d1ecfb8e4125569ac6d66dfab924874921c65e04c93218" + } + ] + }, + { + "targetId": "sha256:8d496c43f132d5f71e97956bbdbc252f3ef26c6615a27f0440cf9b63c1d5ccf4", + "target": "org.jsoup.parser.StreamParser::parse", + "graphSignature": "parse(Reader,String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/parser/StreamParser.java", + "line": 82, + "sha256": "5a50cf810031c426623c68cc630218c3e6725e1519bb039143777f9f19637855" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/parser/StreamParserTest.java", + "line": 456, + "edgeId": "sha256:8785e80cc707d7ab1136876fd42f625164ce09654452177adc3b3003d93a3b06" + } + ] + }, + { + "targetId": "sha256:817144c6469132780623c859650f3dbb59eef49b75108e12e8fd2efe7763b330", + "target": "org.jsoup.safety.Cleaner::isValid", + "graphSignature": "isValid(Document)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 94, + "sha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 230, + "edgeId": "sha256:87c056c51b2d78c239f46136248cabd92a01c20a49c40de2c6b8524ce3385906" + } + ] + }, + { + "targetId": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "target": "org.jsoup.safety.Cleaner::clean", + "graphSignature": "clean(Document)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 62, + "sha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 314, + "edgeId": "sha256:0fe7e24b0bb3f1209889771a740118cec26cdc2af0392852e6b4474a103729de" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 325, + "edgeId": "sha256:cf51e7a56e4b7a6d15e27bdfced0abe6aec269686eabf29e119835f908620d14" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 458, + "edgeId": "sha256:0eaa9ec56cee649aa02a7e908d111590a5a3f8843e64efcf165c36930b9793d3" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 551, + "edgeId": "sha256:d1fe493dfb12d292f0c85130c3dd6212bc7716e5b9b48e2a38187656a82ce810" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 567, + "edgeId": "sha256:f55de48143efe8d27f1357b935ee3dc2408d948ee86ed17cde3e6f57c83e1d47" + } + ] + }, + { + "targetId": "sha256:65e150539d6718f6f78d058c646a266201b50bc1719893aa35c7926097090d94", + "target": "org.jsoup.safety.Safelist::addAttributes", + "graphSignature": "addAttributes(String)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 303, + "sha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 412, + "edgeId": "sha256:059ef0d08b3f6b02cdf6a1f1629b1f61f83db12253a72d711917d34da201af8a" + }, + { + "file": "src/test/java/org/jsoup/safety/CleanerTest.java", + "line": 423, + "edgeId": "sha256:db8ba0c0c548d550b90c730ff00458173536b7ffe58d1dfcda725b3935b2bd1d" + } + ] + }, + { + "targetId": "sha256:012ffde384064b8f0d3f11ca77e7e74ddbcfe7342b24afeb79627a9b194e273f", + "target": "org.jsoup.nodes.Attributes::put", + "graphSignature": "put(Attribute)", + "sourceDeclaration": { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "line": 322, + "sha256": "39551b1d008f2a77b212b5b16e09e257b0ec9d8f118a0b7131a1cfe11ac32875" + }, + "review": "Source supports the exact constructor receiver, selected method declaration, and method-name occurrence. Compared overloads where present. Static source review; no runtime execution claim.", + "occurrences": [ + { + "file": "src/test/java/org/jsoup/safety/SafelistTest.java", + "line": 79, + "edgeId": "sha256:d05163cf82bc8fae9df4666419def2381c1326cf4b38b10db0499fc104523677" + } + ] + } + ] + } + }, + "diagnostic": { + "before": { + "nodes": 30, + "edges": 45, + "methodCalls": 2, + "inventedExternalMethodCalls": 2 + }, + "after": { + "nodes": 28, + "edges": 51, + "sourceSupportedMethodCalls": 9 + }, + "removedFalseRelationships": 3, + "removedInventedExternalNodes": 2, + "cacheUpgradeMatchesClean": true, + "compiler": "javac 17.0.8.1 compiled the fixture; javap descriptors corroborate overload and anonymous/enclosing receiver distinctions. Classes were not executed.", + "depthLimit": "tooDeep compiles to a real Cleaner.check call but exceeds bounded inference; unresolved is not absence credit.", + "enclosingInstance": "outer.new Cleaner() cannot resolve to the unrelated imported lib.Cleaner; candidate and enclosing qualifier remain available as unresolved evidence." + }, + "verification": { + "native": { + "passed": 1439, + "failed": 0, + "ignored": 2, + "scope": "Workspace lib/bin plus selected Java, universal, contract, product, framework and frontend integration tests." + }, + "clippy": "First invocation failed on redundant Ok(...?) in new test. Corrected to direct serialization function; repeated workspace lib/bin and selected integration Clippy passed. Existing unrelated react_frontend test lint violations remain outside this invocation.", + "finalRegression": "One test passed after the test-only Clippy correction.", + "fixturePreflight": "Identical expanded fixed-corpus graphs have zero node/edge changes excluding communities; no topology bounds changed.", + "fullFixtureQualification": "Running; do not treat prior route-parent qualification as Java verification.", + "formatDiffProductBoundary": "cargo fmt --all -- --check, git diff --check, and scripts/check_product_boundary.sh passed." + }, + "limitations": [ + "Text scores stay 46/55 each; selected relationships are 17/21 Compass versus 20/21 Graphify; reviewed occurrences tie at 17/21; paths remain 6/10 versus 8/10.", + "Single shared-machine timing run overlaps verification; latency is descriptive and gives no controlled performance comparison. Graphify digest attests its launcher only.", + "Anonymous dispatch, enclosing-instance owner resolution, arrays/casts, chained returns, inherited methods and deeper parentheses are not established by this change.", + "Source review found existing incomplete varargs display signatures: addTags() represents addTags(String...), and addAttributes(String) omits its String... parameter. Exact target source declarations, rather than those incomplete signatures, justify these edges.", + "No new MCP, god-object responsibility, community cohesion or directed/long-path superiority evidence. Historical pinned hierarchy scorecards remain invalidated." + ], + "artifactBase": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926", + "artifacts": { + "java-constructor-jsoup-delta.json": "f147ad06393c3f5b1600a90131b910ca6bca2c75d821ef3c4cb1a5c8907502d7", + "java-constructor-jsoup-source-rows.json": "7f60a3b7f42d14a4958ba0a6b7b90c6df5d116baf557b2b9a5643a944fd8e33d", + "java-constructor-receiver-final/policy-with-enclosing-negative.json": "17aeb86caa602f7a73ec3ea8a70e01ea17bc82547b5c5d64ad63ae3c61e62378", + "java-constructor-receiver-final/compiler-review.json": "8241b055f2a52a80007924342dde93d766d97aff3a6a034fcf7c54e734c83e6a", + "java-constructor-receiver-final/javap.txt": "327ac29b9c1ef0358bfd6ddaa3e30ff1f2b4bb63698408c8bb6e1338018aa048", + "java-constructor-receiver-final/delta.json": "7ec95282d16ce034ee8530e799eb751030c2fe8bf883dda8209a59465f323c9c", + "java-constructor-receiver-final/cache-review.json": "c57a9206395804a8085e97613beac43c9c858513099df911f62e33fa15b1d6ad", + "java-constructor-fixture-delta.json": "4b5ee7e5b75c5a93507fb2b92a382fadae6130b45b5600a292c05fa56d6101a8", + "java-constructor-native-01.log": "6d158f4b63b730028bcf940249aa626e4bf00ae5171ec9d102eaee544df9063d", + "java-constructor-clippy-01.log": "6904caf5e5c2ea6afbfd54469a572420b53b53e2477d1ec91236c81984f1ab4a", + "java-constructor-clippy-02.log": "2d48f6b34ae40fdc9a8d76136f5bffa25d233826932b0cc7aed55fd5932a63d6", + "java-constructor-native-after-04.log": "9122a15b8123f1d82b9558cd539da9bfb72ee8ee533753516166bc0f3ab7a68f", + "java-constructor-build-01.log": "cca4bf37bd1942503d72f28a0145be1717ef6747222ab3a359daf1c6d7f1761f", + "runs/java-constructor-panel-a-01/run.json": "c900349960adfa6b488ce98397529b5e55accb85e0dbf5dd4713a5a8b1a0bbf5", + "java-constructor-panel-a-path-audit-01.json": "e598f0e63e29be41883a0702ae01f828e0d63849693262afaa73ce9e25b4ef7e", + "java-constructor-panel-a-chi-audit-01.json": "43343bf82615be5ab0d137b233aeddf1ede61960ce6607b6d1d9197af35840a5", + "java-constructor-panel-a-click-audit-01.json": "9ba5d437fa949e3fc12449b2eb451caa9c0490a797d25c6e58520c813d15b0fa", + "java-constructor-panel-a-jsoup-audit-01.json": "2727f4c114cbbecd6dc261a5b0d9aa84fa576d90e831522ebd95f3e5e79d0b71", + "java-constructor-panel-a-redux-audit-01.json": "3373cda48df45a5a422f5feb8afa1cdb80c461bd8f4b10b560303ba383d0c1ed", + "java-constructor-panel-a-walkdir-audit-01.json": "3709a54138a721680b999acafeb0c30204b9f16b1cd05fb5fd060f238e5c01f1" + } +} diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index 58d606e78..8e46267c3 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "4"; +pub const AST_CACHE_VERSION: &str = "5"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index 765183efe..5654c63fb 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -3669,12 +3669,10 @@ impl<'source> DirectEvidenceState<'source> { if spelling.is_empty() { return Ok(()); } - let receiver = node - .child_by_field_name("object") - .map(|object| self.text(object)); - let receiver_type = receiver - .as_deref() - .and_then(|receiver| self.java_receiver_type(owner, receiver, node.start_byte())); + let receiver_node = node.child_by_field_name("object"); + let receiver = receiver_node.map(|object| self.text(object)); + let receiver_type = + receiver_node.and_then(|object| self.java_method_receiver_type(owner, object, 0)); let qualified_name = receiver_type .as_ref() .map(|receiver| format!("{receiver}::{spelling}")); @@ -3731,18 +3729,35 @@ impl<'source> DirectEvidenceState<'source> { if spelling.is_empty() { return Ok(()); } - let qualified_name = self.java_qualified_type(owner, &normalized, type_node.start_byte()); + let mut cursor = node.walk(); + let enclosing_receiver = node + .children(&mut cursor) + .find(|child| !child.is_extra()) + .filter(|child| child.kind() != "new") + .map(|receiver| self.text(receiver)); + // A qualified instance creation looks up the member class through + // its enclosing receiver. An import with the same terminal name + // cannot establish that ownership. Retain an unresolved occurrence + // until the enclosing type can be proven. + let qualified_name = if enclosing_receiver.is_some() { + None + } else { + self.java_qualified_type(owner, &normalized, type_node.start_byte()) + }; let lookup = qualifier.map(qualified_binding_head).unwrap_or(spelling); - let binding = self - .binding_for_occurrence(owner, lookup, type_node.start_byte(), true) - .cloned(); + let binding = if enclosing_receiver.is_some() { + None + } else { + self.binding_for_occurrence(owner, lookup, type_node.start_byte(), true) + .cloned() + }; let argument_count = java_argument_count(node); let argument_types = self.java_argument_types(node, owner); let occurrence_id = self.builder.occur_with_context( SemanticRole::Construction, &owner.fact_id, spelling, - qualifier, + enclosing_receiver.as_deref().or(qualifier), Some(&owner.scope_id), Some(&format!("arity:{argument_count}")), range_for_node(self.source_file, type_node), @@ -3763,7 +3778,7 @@ impl<'source> DirectEvidenceState<'source> { argument_types, allowed_target_kinds: vec!["class".to_owned(), "record".to_owned()], hierarchy: None, - allow_external: true, + allow_external: enclosing_receiver.is_none(), }, )?; Ok(()) @@ -3888,6 +3903,51 @@ impl<'source> DirectEvidenceState<'source> { } } + fn java_method_receiver_type( + &self, + owner: &DeclarationContext, + expression: Node<'_>, + depth: usize, + ) -> Option { + if depth >= 8 || expression.has_error() { + return None; + } + match expression.kind() { + "parenthesized_expression" => { + let mut cursor = expression.walk(); + expression + .named_children(&mut cursor) + .find(|child| !child.is_extra()) + .and_then(|inner| self.java_method_receiver_type(owner, inner, depth + 1)) + } + "object_creation_expression" => { + let mut cursor = expression.walk(); + // An enclosing-instance creation (`outer.new Inner()`) needs + // its own owner resolution. Anonymous classes may override + // methods on the named base; neither proves a base call. + if expression + .children(&mut cursor) + .find(|child| !child.is_extra()) + .is_none_or(|child| child.kind() != "new") + || expression + .named_children(&mut cursor) + .any(|child| child.kind() == "class_body") + { + return None; + } + let target = expression.child_by_field_name("type")?; + self.java_qualified_type(owner, &self.text(target), target.start_byte()) + } + "identifier" | "this" | "super" | "field_access" => { + self.java_receiver_type(owner, &self.text(expression), expression.start_byte()) + } + // Do not normalize arbitrary receiver expressions as type text: + // it invents targets such as `newlib.Cleaner::check`. Arrays, + // casts, and chained results need separate type/dispatch proof. + _ => None, + } + } + fn java_receiver_type( &self, owner: &DeclarationContext, diff --git a/crates/compass-resolve/tests/java_constructor_receivers.rs b/crates/compass-resolve/tests/java_constructor_receivers.rs new file mode 100644 index 000000000..9cb8af418 --- /dev/null +++ b/crates/compass-resolve/tests/java_constructor_receivers.rs @@ -0,0 +1,248 @@ +use std::collections::{BTreeMap, HashMap}; +use std::error::Error; +use std::path::Path; + +use compass_languages::{CandidateRelation, Engine, EvidenceLimits, validate_evidence}; +use compass_resolve::resolve; + +const SOURCES: [(&str, &str); 3] = [ + ( + "lib/Cleaner.java", + r#"package lib; +public class Cleaner { + public boolean check(String value) { return true; } + public boolean check(int value) { return false; } + public Other next() { return new Other(); } + public String toString() { return "cleaner"; } +} +"#, + ), + ( + "lib/Other.java", + r#"package lib; +public class Other { + public boolean check(String value) { return false; } +} +"#, + ), + ( + "app/Use.java", + r#"package app; +import lib.Cleaner; +public class Use { + public boolean direct() { new Cleaner().check("first"); return new Cleaner().check("second"); } + public boolean parenthesized() { return (new Cleaner()).check("ok"); } + public boolean qualified() { return new lib.Cleaner().check("ok"); } + public boolean commented() { return (/* receiver */ new Cleaner(/* args */)).check("ok"); } + public boolean nested() { return (((new Cleaner()))).check("ok"); } + public boolean typeNamespace(String Cleaner) { return new Cleaner().check("ok"); } + public boolean tooDeep() { return (((((((((new Cleaner()))))))))).check("ok"); } + public String array() { return new Cleaner[0].toString(); } + public String casted() { return ((Object) new Cleaner()).toString(); } + public boolean qualifiedAnonymous() { return new lib.Cleaner() { + public boolean check(String value) { return false; } + }.check("ok"); } + public boolean overloaded() { return new Cleaner().check(1); } + public boolean anonymous() { return new Cleaner() { + public boolean check(String value) { return false; } + }.check("ok"); } + public boolean chained() { return new Cleaner().next().check("ok"); } + public boolean explicitOuter(Outer outer) { return outer.new Cleaner().check("ok"); } +} +class Outer { + class Cleaner { public boolean check(String value) { return false; } } +} +"#, + ), +]; + +#[test] +fn java_constructor_receivers_preserve_overloads_occurrences_and_unknown_results() +-> Result<(), Box> { + let sources = SOURCES + .into_iter() + .map(|(path, source)| (path.to_owned(), source.to_owned())) + .collect::>(); + let mut engine = Engine::default(); + let mut extractions = SOURCES + .into_iter() + .map(|(path, source)| engine.extract_source(Path::new(path), source.as_bytes())) + .collect::, _>>()?; + let evidence = extractions[2] + .semantic_evidence + .as_ref() + .ok_or("missing Java evidence")?; + validate_evidence(evidence, EvidenceLimits::default())?; + let enclosing_owner = evidence + .declarations + .iter() + .find(|declaration| declaration.name == "explicitOuter") + .ok_or("missing enclosing-instance caller")?; + let enclosing_constructions = evidence + .candidates + .iter() + .filter(|candidate| { + candidate.relation == CandidateRelation::Constructs + && candidate.source_declaration_id == enclosing_owner.id + }) + .collect::>(); + assert_eq!(enclosing_constructions.len(), 1); + let enclosing = enclosing_constructions[0]; + assert!(enclosing.binding_id.is_none()); + assert!(enclosing.constraints.qualified_name.is_none()); + assert!(!enclosing.constraints.allow_external); + let enclosing_occurrence = evidence + .occurrences + .iter() + .find(|occurrence| Some(&occurrence.id) == enclosing.occurrence_id.as_ref()) + .ok_or("missing enclosing-instance occurrence")?; + assert_eq!(enclosing_occurrence.qualifier.as_deref(), Some("outer")); + let mut call_sites = BTreeMap::>::new(); + for candidate in &evidence.candidates { + if candidate.relation != CandidateRelation::Calls + || !matches!(candidate.target_spelling.as_str(), "check" | "toString") + { + continue; + } + let owner = evidence + .declarations + .iter() + .find(|declaration| declaration.id == candidate.source_declaration_id) + .ok_or("missing call owner")?; + let occurrence = evidence + .occurrences + .iter() + .find(|occurrence| Some(&occurrence.id) == candidate.occurrence_id.as_ref()) + .ok_or("missing call occurrence")?; + let source_text = sources + .get(&occurrence.range.source_file) + .ok_or("missing occurrence source")?; + let start = usize::try_from(occurrence.range.start_byte)?; + let end = usize::try_from(occurrence.range.end_byte)?; + assert_eq!( + source_text.get(start..end), + Some(candidate.target_spelling.as_str()), + "call occurrences must retain the exact method-name span" + ); + call_sites + .entry(owner.name.clone()) + .or_default() + .push(occurrence); + let expected = match owner.name.as_str() { + "direct" | "parenthesized" | "qualified" | "overloaded" | "commented" | "nested" + | "typeNamespace" => Some("lib.Cleaner::check"), + "anonymous" | "chained" | "tooDeep" | "array" | "casted" | "qualifiedAnonymous" + | "explicitOuter" => None, + other => return Err(format!("unexpected check owner: {other}").into()), + }; + assert_eq!( + candidate.constraints.qualified_name.as_deref(), + expected, + "{}", + owner.name + ); + } + assert_eq!(call_sites.len(), 14); + assert_eq!(call_sites["direct"].len(), 2); + assert_ne!(call_sites["direct"][0].id, call_sites["direct"][1].id); + + let resolved = resolve(&extractions, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + assert!( + !resolved.edges.iter().any(|edge| { + edge.string("rule").starts_with("universal-construction-") + && resolved.nodes.iter().any(|node| { + node.id == edge.source + && node.string("qualified_name") == "app.Use::explicitOuter" + }) + && resolved.nodes.iter().any(|node| { + node.id == edge.target && node.string("qualified_name") == "lib.Cleaner" + }) + }), + "outer.new Cleaner() must not instantiate the unrelated imported Cleaner" + ); + let mut calls = BTreeMap::>::new(); + for edge in &resolved.edges { + // Raw construction projection also uses `calls` before publication + // normalizes it to `instantiates`; inspect method-call candidates here. + if edge.string("relation") != "calls" || !edge.string("rule").starts_with("universal-call-") + { + continue; + } + let source = resolved + .nodes + .iter() + .find(|node| node.id == edge.source) + .ok_or("missing source")?; + let target = resolved + .nodes + .iter() + .find(|node| node.id == edge.target) + .ok_or("missing target")?; + if source.string("qualified_name").starts_with("app.Use::") { + assert_eq!(edge.string("_origin"), "ast"); + assert_eq!(edge.string("confidence"), "EXTRACTED"); + calls + .entry(source.string("qualified_name")) + .or_default() + .push(format!( + "{}|{}", + target.string("qualified_name"), + target.string("signature") + )); + } + } + for targets in calls.values_mut() { + targets.sort(); + } + assert_eq!( + calls, + BTreeMap::from([ + ( + "app.Use::direct".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned(); 2] + ), + ( + "app.Use::parenthesized".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned()] + ), + ( + "app.Use::qualified".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned()] + ), + ( + "app.Use::commented".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned()] + ), + ( + "app.Use::nested".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned()] + ), + ( + "app.Use::typeNamespace".to_owned(), + vec!["lib.Cleaner::check|check(String)".to_owned()] + ), + ( + "app.Use::overloaded".to_owned(), + vec!["lib.Cleaner::check|check(int)".to_owned()] + ), + ( + "app.Use::chained".to_owned(), + vec!["lib.Cleaner::next|next()".to_owned()] + ), + ]) + ); + extractions.reverse(); + let reversed = resolve(&extractions, &sources); + let canonical = |graph: &compass_languages::Extraction| { + let mut edges = graph + .edges + .iter() + .map(serde_json::to_string) + .collect::, serde_json::Error>>()?; + edges.sort(); + Ok::<_, serde_json::Error>(edges) + }; + assert_eq!(canonical(&resolved)?, canonical(&reversed)?); + Ok(()) +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index c6f8953a0..ac588a907 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1506,27 +1506,72 @@ Markdown, lifecycle determinism, and release frontend qualification stages. This full pass qualifies production commit `c20db15b`; the pinned hierarchy scorecards remain invalidated and require separate source review. -#### Java constructor receiver diagnostic - -A separate frozen-binary diagnostic reproduces the jsoup constructor receiver -gap in direct, parenthesized, fully qualified, and overloaded method calls. -Anonymous-class and chained-result negatives remain unresolved. A native -resolver regression also fails because the direct method candidate lacks -`lib.Cleaner::check` as its receiver constraint. Its source and failure log are -retained in `java-constructor-receiver-diagnostic` and -`java-constructor-receiver-test-01.log`; the test is not installed as a passing -repository test, and no Java production change or score improvement is claimed. -The first ad hoc graph review used the raw-extraction `relation` key instead -of graph-v1 `kind`; `before-review-corrected.json` records the corrected review. -The native evidence and corrected graph inspection agree on the missing calls. +#### Java constructor receiver correction + +The earlier frozen-binary diagnostic and failed native regression reproduced +the jsoup constructor receiver gap. They remain retained, including the initial +graph-review mistake (`relation` versus graph-v1 `kind`). The producer now reads +the constructor's AST type for direct calls and up to seven parenthesis wrappers, +then uses existing qualified-type and overload resolution. It rejects anonymous +and enclosing-instance receiver assumptions. A separate correction retains +`outer.new Cleaner()` as unresolved instead of binding an unrelated imported +`Cleaner`. AST cache semantics advance from 4 to 5; product version, graph and +evidence schemas, and advertised capabilities are unchanged. + +On identical three-file Java diagnostic sources, the old binary produces two +invented external method targets. The correction removes those two nodes and +three false relationships, and adds nine source-supported calls. The native +regression checks exact name spans, repeated occurrences, argument overloads, +type/value namespace distinction, provenance, direction, and reverse-input +determinism. `javac`/`javap` independently corroborate the fixture's target +distinctions; compiled classes were not executed. The deliberately deep receiver +is a real call beyond bounded inference, **not a true absence negative**. A copy +of the old cache rebuilds to a graph byte-identical to a clean extraction. + +Fresh paired builds at the same five repository pins and all 110 unchanged CLI +requests are recorded in `java-constructor-panel-a-01`. The other four Compass +graphs are byte-identical to the preceding checkpoint. jsoup retains all 6,116 +nodes and common edges, except community assignments, and adds **122 call +occurrences** (10 production, 112 test). Complete delta review checks each +receiver, package/import context, target declaration, overload argument types, +source span, and direction. This is static review of a development delta, not a +representative precision sample or proof of runtime calls. It also exposes +existing incomplete varargs display signatures; the exact source declarations +support those edges, while signature completeness remains follow-up work. + +| Measure | Compass | Graphify | +| --- | ---: | ---: | +| Query text oracle | 46/55 | 46/55 | +| Source-checked paths | 6/10 | 8/10 | +| Selected source relationships | 17/21 | 20/21 | +| All reviewed occurrences, corrected Click oracle | 17/21 | 17/21 | + +The one newly matched selected pair is `Jsoup.isValid` to +`Cleaner.isValidBodyHtml`; both tools previously missed it. The +[development review](../../benchmarks/agent_query/java_constructor_development_review.json) +records executable, graph, source and artifact hashes, all added occurrences, +target declarations, and remaining limitations. Reusing these repositories after +they informed the fix is development evidence, even though the suite filename +contains `heldout`. No new MCP or god-object/community quality claim follows. +Timings overlap native verification and support no speed claim. + +Verification: **1,439 native tests passed, zero failed, two ignored**. Clippy first +found redundant error wrapping in the new test; after that test-only correction, +workspace library/binary plus selected integration Clippy and the focused +regression pass. The fixed qualification corpus has zero node/edge changes +excluding communities, so no topology thresholds changed. Full Java fixture +qualification is running; the preceding route-parent pass does not verify this +Java production change. ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. The corrected fixture and selected real-source evidence above do not replace those broader checks. -2. Extend source-proven constructor, loop/result/iterator inference to recover - the jsoup and fd misses. Keep exact +2. Extend source-proven loop/result/iterator inference and TypeScript declaration + identity to recover the remaining fd and Redux misses. Address Java varargs + signature completeness and unresolved receiver forms with separate evidence. + Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. 3. Use the source-role census and connectivity breakdowns to review actual diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index f4be59dd7..1105ff6a5 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -202,6 +202,19 @@ of acquiring an invented target. This correctness correction keeps the existing producer capability contract; AST cache semantics version 3 prevents reuse of facts produced before it. +Java method-call candidates can derive their receiver from direct named object +creation, including fully qualified types and parenthesized forms. The method +occurrence remains the exact method-name span, and the existing argument vector +selects among overloads. The producer unwraps at most seven parenthesis levels; +deeper forms retain an unresolved candidate. This bounded inference limit is +not evidence that no call exists. Anonymous classes, enclosing-instance +creation, arrays, casts, and unproven chained results do not acquire a target +from punctuation-stripped expression text. Enclosing-instance construction +retains its receiver qualifier and stays unresolved without owner evidence, +even if a same-named class is imported. AST cache semantics version 5 prevents +reuse of the previous extraction; producer capabilities and schemas are +unchanged. + Rust producer version 2 follows fields through source-proven standard-library `Arc`, `Rc`, and `Box` dereference wrappers and carries a unique source-visible call-result type into the next member call. It also inspects a local From d71c389bb3a142fe44f79019aea0255e65821ee8 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 21:09:52 -0700 Subject: [PATCH 32/97] docs: record passing Java fixture qualification --- .../agent_query/java_constructor_development_review.json | 8 +++++--- .../code-graph-intelligence-audit-2026-09-26.md | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/benchmarks/agent_query/java_constructor_development_review.json b/benchmarks/agent_query/java_constructor_development_review.json index 5acc786fd..e77e8e171 100644 --- a/benchmarks/agent_query/java_constructor_development_review.json +++ b/benchmarks/agent_query/java_constructor_development_review.json @@ -1146,8 +1146,9 @@ "clippy": "First invocation failed on redundant Ok(...?) in new test. Corrected to direct serialization function; repeated workspace lib/bin and selected integration Clippy passed. Existing unrelated react_frontend test lint violations remain outside this invocation.", "finalRegression": "One test passed after the test-only Clippy correction.", "fixturePreflight": "Identical expanded fixed-corpus graphs have zero node/edge changes excluding communities; no topology bounds changed.", - "fullFixtureQualification": "Running; do not treat prior route-parent qualification as Java verification.", - "formatDiffProductBoundary": "cargo fmt --all -- --check, git diff --check, and scripts/check_product_boundary.sh passed." + "fullFixtureQualification": "Passed with exit 0: native scale ceilings, source semantics/topology, cold/warm/forced/alternate-checkout/delete/rename/restore determinism, Markdown checks, release build and frontend precedence/positive/negative qualification. Production commit 22814e59.", + "formatDiffProductBoundary": "cargo fmt --all -- --check, git diff --check, and scripts/check_product_boundary.sh passed.", + "releaseBinarySha256": "88f98577c865a92bf6eb9cb7dc506eb5c806a2a66909d68c5fa1d1db43ccfb8d" }, "limitations": [ "Text scores stay 46/55 each; selected relationships are 17/21 Compass versus 20/21 Graphify; reviewed occurrences tie at 17/21; paths remain 6/10 versus 8/10.", @@ -1177,6 +1178,7 @@ "java-constructor-panel-a-click-audit-01.json": "9ba5d437fa949e3fc12449b2eb451caa9c0490a797d25c6e58520c813d15b0fa", "java-constructor-panel-a-jsoup-audit-01.json": "2727f4c114cbbecd6dc261a5b0d9aa84fa576d90e831522ebd95f3e5e79d0b71", "java-constructor-panel-a-redux-audit-01.json": "3373cda48df45a5a422f5feb8afa1cdb80c461bd8f4b10b560303ba383d0c1ed", - "java-constructor-panel-a-walkdir-audit-01.json": "3709a54138a721680b999acafeb0c30204b9f16b1cd05fb5fd060f238e5c01f1" + "java-constructor-panel-a-walkdir-audit-01.json": "3709a54138a721680b999acafeb0c30204b9f16b1cd05fb5fd060f238e5c01f1", + "java-constructor-qualification-01.log": "611c2b5b4fcb60e372598a406810e4a43343af765234f04ab2d82156cf816b5d" } } diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index ac588a907..d55d60079 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1560,8 +1560,10 @@ found redundant error wrapping in the new test; after that test-only correction, workspace library/binary plus selected integration Clippy and the focused regression pass. The fixed qualification corpus has zero node/edge changes excluding communities, so no topology thresholds changed. Full Java fixture -qualification is running; the preceding route-parent pass does not verify this -Java production change. +qualification completed with exit 0, including native scale ceilings, semantic +and topology checks, lifecycle determinism, Markdown checks, the release build, +and frontend precedence/positive/negative checks. This pass qualifies Java +production commit `22814e59`; it does not establish comparative superiority. ## Next evidence to collect From 9b63873ac98c0401cc20209e659ed40206a10269 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 21:30:28 -0700 Subject: [PATCH 33/97] fix: resolve proven export bindings without discarding ambiguity --- CHANGELOG.md | 5 + COMPATIBILITY.md | 24 ++ MIGRATION.md | 6 + .../export_binding_development_review.json | 232 ++++++++++++ crates/compass-model/src/document.rs | 51 +-- .../tests/traversal_confidence.rs | 41 +++ crates/compass-query/src/code_query.rs | 44 ++- crates/compass-query/src/export_binding.rs | 299 +++++++++++++++ crates/compass-query/src/lib.rs | 1 + crates/compass-query/src/score.rs | 5 +- .../tests/export_binding_resolution.rs | 341 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 61 +++- 12 files changed, 1082 insertions(+), 28 deletions(-) create mode 100644 benchmarks/agent_query/export_binding_development_review.json create mode 100644 crates/compass-model/tests/traversal_confidence.rs create mode 100644 crates/compass-query/src/export_binding.rs create mode 100644 crates/compass-query/tests/export_binding_resolution.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f96ff9a87..a3dcb6049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Resolve coincident export-binding and declaration name matches through exact + export evidence, improving symbol-based paths and relationship queries while + preserving genuine ambiguity and exact-ID selection. Traversal caches retain + the weakest relationship confidence and deferred state. + - Resolve Java method receivers constructed directly at the call site, including qualified types and bounded parentheses, while retaining overload and occurrence evidence. Stop turning arbitrary receiver expressions into diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index fa6ecf880..aff14f4cc 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -306,6 +306,30 @@ that parsed caveat text from continuation pages must read it from page one. ### TypeScript path aliases and file-shaped path input +Exact name lookup can identify both an export binding and its declaration. +When complete, bounded graph evidence proves that the binding's exact source +range belongs to one owner and exports one matching declaration, lookup removes +the redundant binding candidate. Both records retain their graph identities; +an exact export ID still selects the binding. The declaration must already be +in the exact-name candidate set and share the binding's qualified name, +normalized name, and source file. This applies to typed relationship/trail +queries and the exact lookup used by paths, explanations, and MCP navigation. + +Different declarations, multiple export targets, incomplete source ranges, +inferred/ambiguous/deferred evidence, and incomplete candidate sets keep their +ambiguity. Additional proof examines at most 256 candidates and 1,024 adjacency +entries plus a truncation probe. Exhaustion retains the original candidates; +typed responses report truncation and an ambiguity diagnostic. Graph schemas, +stored identities, and extraction are unchanged. Existing graphs can receive +this lookup correction without re-extraction. + +Traversal cache format advances from `TRAILT05` to `TRAILT06` to retain deferred +relationship flags and the weakest confidence across all evidence, including +explicit compatibility confidence. Missing or unknown confidence values in an +evidence item cannot establish an exact fact. Older disposable traversal +caches rebuild from the authoritative graph; published historical graphs are +not rewritten. + A TypeScript or JavaScript project that is the `extends` base of another project keeps its own `compilerOptions.paths` when it declares `files` or `include`. Only a base config without its own file set stays excluded, and a diff --git a/MIGRATION.md b/MIGRATION.md index 3247673a8..88c8ed211 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,12 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Name-based queries can now resolve a coincident export binding to its proven +declaration. Use the exact export node ID when you want the binding record. +This query correction works on existing graphs. Older traversal caches rebuild +automatically to retain mixed-confidence and deferred relationship evidence; +graph re-extraction is not required for that cache correction. + Rebuild Java graphs to receive the constructor-receiver correction. Direct constructor method calls can gain source-proven targets; invented external targets and unrelated imported-class construction edges can disappear. Normal diff --git a/benchmarks/agent_query/export_binding_development_review.json b/benchmarks/agent_query/export_binding_development_review.json new file mode 100644 index 000000000..f4e81537d --- /dev/null +++ b/benchmarks/agent_query/export_binding_development_review.json @@ -0,0 +1,232 @@ +{ + "schema": "compass.export-binding-development-review/1", + "scope": "Repeated five-language development comparison; queries and repositories have informed changes. Not held-out evidence, full-answer precision, representative extraction precision, or performance superiority.", + "runId": "export-binding-panel-a-01", + "baselineRunId": "java-constructor-panel-a-01", + "suiteDigest": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "runnerDigest": "173242988699eb6fabd175c293634d3cf37c1394f76429540fd7af2a48ef7026", + "tools": [ + { + "binary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/export-binding-resolution/compass", + "binarySha256": "04eab05bd5bbc94f3f0f86580bdd3de9950599bc9e548b8f8ba95791b8baa0c9", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30" + }, + { + "binary": "/Users/haipingfu/.local/bin/graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67" + } + ], + "baseCommit": "d71c389bb3a142fe44f79019aea0255e65821ee8", + "sourceSha256": { + "crates/compass-model/src/document.rs": "44d4f127600283af1adb32a378fcd233b22679ee21b32ab7335f76b4c25dfb6d", + "crates/compass-query/src/lib.rs": "e278a19f408edabd934711d725c06c357d590e3e5cf3d971515783b517167540", + "crates/compass-query/src/code_query.rs": "738e811194bdf0fac0593c58a594aa6cd7da43209b07d95833864e2cbe1ce856", + "crates/compass-query/src/score.rs": "e6e63abff3864a6a0a6d9e9b2a96e366bcb2317c1e0b1acc1fa4cae9fabe60d0", + "crates/compass-query/src/export_binding.rs": "f8da01cb357be0b85d140b941e5ea6ceeb602523e4b61242c021d6d750a36dc8", + "crates/compass-model/tests/traversal_confidence.rs": "b148d9f96e0cee987392d2e9be712cef01d6d65a8b17d4b988c6989cce729f83", + "crates/compass-query/tests/export_binding_resolution.rs": "0a5afcf41ae417f25d791ac8acb16cc2276333e50d06ee4a98e6294bf1e90c77" + }, + "metrics": { + "compass": { + "text": { + "medianAnswerTokens": 148.0, + "medianFirstPageTokens": 148.0, + "medianWallMs": 765.0, + "passRate": 0.8909, + "passed": 49, + "questions": 55 + }, + "selectedSourceRelationships": { + "passed": 17, + "allOccurrencesPassed": 17, + "total": 21 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + } + }, + "graphify": { + "text": { + "medianAnswerTokens": 104.0, + "medianFirstPageTokens": 75.0, + "medianWallMs": 150.0, + "passRate": 0.8364, + "passed": 46, + "questions": 55 + }, + "selectedSourceRelationships": { + "passed": 20, + "allOccurrencesPassed": 17, + "total": 21 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + } + } + }, + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf", + "compassGraphByteIdenticalToBaseline": true, + "graphifyGraphByteIdenticalToBaseline": true + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234", + "compassGraphByteIdenticalToBaseline": true, + "graphifyGraphByteIdenticalToBaseline": true + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "d69152cc8900afc3f11756b8ce841adbed98ae6663fee4ebc64957974c8f860c", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69", + "compassGraphByteIdenticalToBaseline": true, + "graphifyGraphByteIdenticalToBaseline": true + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b", + "compassGraphByteIdenticalToBaseline": true, + "graphifyGraphByteIdenticalToBaseline": true + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd", + "compassGraphByteIdenticalToBaseline": true, + "graphifyGraphByteIdenticalToBaseline": true + } + ], + "textOracleDelta": [ + { + "repository": "redux", + "tool": "compass", + "question": "redux-holdout-callees", + "before": false, + "after": true + }, + { + "repository": "redux", + "tool": "compass", + "question": "redux-holdout-path-forward", + "before": false, + "after": true + }, + { + "repository": "redux", + "tool": "compass", + "question": "redux-holdout-path-reverse", + "before": false, + "after": true + } + ], + "queryChange": { + "proof": "Remove a redundant export candidate only with complete exact nondeferred contains/exports occurrence evidence, one target already in the candidate set, matching normalized name and nonempty qualified name, and same source file.", + "limits": { + "candidates": 256, + "examinedEdges": 1024, + "truncationProbe": 1 + }, + "ambiguity": "Missing, conflicting, coarse, deferred, weaker-confidence, or exhausted evidence retains ambiguity. Exact-ID lookup remains available for export bindings.", + "graphChange": "None: all five Compass graph files are byte-identical. Published identities/relationships and AST cache semantics 5 are unchanged.", + "traversalCache": "TRAILT06 replaces disposable TRAILT05; complete evidence confidence and deferred state survive compact projection.", + "reviewedSourceCensus": { + "reduxCoincidentBindings": 126, + "otherFourRepositories": 0, + "limitation": "Post-output static binding/declaration correspondence review, not 126 successful public queries or a representative precision estimate." + } + }, + "verification": { + "native": { + "passed": 1180, + "failed": 0, + "ignored": 2, + "scope": "Workspace lib/bin plus export_binding_resolution, traversal_confidence, code_traversal, store_engine, code_query_cli, compass_product; all --locked." + }, + "clippy": "Workspace lib/bin plus export_binding_resolution and traversal_confidence, --locked, -D warnings: passed.", + "newRegressionCounts": { + "exportBinding": 4, + "traversalCache": 1 + }, + "regressionSurfaces": [ + "JSON graph", + "generic materialized store", + "direct SQLite reader", + "cold and warm compact projection", + "stale cache rebuild", + "forward/reversed graph order", + "exact export ID", + "genuine ambiguity", + "candidate truncation", + "proof work exhaustion", + "coarse source location", + "weakest mixed confidence and deferred state" + ], + "fullFixtureQualification": "Running separately; previous Java full pass does not qualify this query/cache change." + }, + "remainingLimits": [ + "WalkDir ordinary-name path witnesses fail both tools; Compass retains genuine ambiguity and Graphify returns an unreviewed route.", + "Original Redux relationship identity oracle still fails four pairs because distinct export/function records remain; do not relabel these as missing calls or replace the oracle after observing results.", + "Click corrected occurrence witness remains a post-output diagnostic; original witness history is preserved.", + "Source paths are limited to previously selected short witnesses; directed and long paths remain unqualified.", + "No new MCP or source-responsibility/god-object/community-quality evidence in this run.", + "Shared-machine query timings overlap native verification; no speed or cost superiority claim.", + "General superiority and full-answer precision remain unproven." + ], + "artifactBase": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926", + "artifactsSha256": { + "export-binding-resolution/coincident-binding-census.json": "baa84957b4d2fd79a177fbd868310426a4a5cf2b9a713b1d047a4798f832ef7d", + "export-binding-resolution/confidence-census.json": "045d11e24b9927a760654cf7620caf651384cfbe463739a490ac7640b6ec9580", + "export-binding-resolution/manifest.json": "659c8d97a08ad9c90f9815c851f2f1cf4c2cea2e2954e3932a9d7ddd1debb6c8", + "export-binding-resolution/panel-a-chi-audit-01.json": "38d8758985e319e059b6e366e29ed8720c0e6ba8f2cb5ab0202e6c7efbdb59c1", + "export-binding-resolution/panel-a-click-audit-01.json": "46fa5eb5468eefe475d83f0f510d45b3f9d7f3e9f2e45916ef303a535472a87c", + "export-binding-resolution/panel-a-jsoup-audit-01.json": "7a679e6a705c86b9ede6359828f6ba9b47021bd8532c01fb88adb909637bd20f", + "export-binding-resolution/panel-a-path-audit-01.json": "a949ce75bbc98e982c51fe24fcca8dabc7e3a477388e3e3a1e9edf5bd4d08508", + "export-binding-resolution/panel-a-redux-audit-01.json": "279d10f15c3c875b45bef0948ee606a37eb18ee6fe5a1d92de298726905aa6b0", + "export-binding-resolution/panel-a-walkdir-audit-01.json": "3a4859150163e6f41cb6e4ec421bf112da5ac1b3e9568662c15270dbf55d061d", + "export-binding-resolution/policy.json": "19b85a2082136d8bce5694e05aa7082ba9f6316d61c26a2b70446cd018dd78da", + "export-binding-resolution/redux-binding-source-rows.json": "5c41ea508e29f6abd723dd6ddf33849958361da9af474904258f2e221c12d971", + "export-binding-resolution/build-01.log": "337f2e5fab181a48d2f326cb7b93a44fc0dd3d0551904ea80c45e309f33e6915", + "export-binding-resolution/cache-after-01.log": "6c21340925799b40b3cac520020c3ca9582ecc14f1f3fab29237bfe6e1de2d5a", + "export-binding-resolution/cache-after-02.log": "8bc84459762d872d85d8e48d6bd6436e17238882ced91936d22fafc59aca7d7a", + "export-binding-resolution/cache-after-03.log": "417e780cf9134f02f42e993fe1f3f5634da1fc55f25ac577e0cef9caed2717ae", + "export-binding-resolution/cache-before.log": "1902cc5e43b986ce35fe5cf379b892b4a4d09cf33858435bc30124ef81fdbccc", + "export-binding-resolution/clippy-01.log": "7fe13ed49c54c46adfde630793497e93676224e421d5e4225a398853e1626e40", + "export-binding-resolution/clippy-broad-01.log": "b72c1ff1f61423bc4f887a70bba44054cc2cd7aa52fa27d972367649f3a58e3a", + "export-binding-resolution/initial-format.log": "89441c383180099db4e4450c20a6d435e28cc007f3fd956aa6a6ec12de8dccc6", + "export-binding-resolution/native-after-01.log": "feb5b2b287d146551b0d242a4713edc2c9d57ed7144c64c75e2b20ca635ebf88", + "export-binding-resolution/native-after-02.log": "2b32cf27ae84c6dec132802c5d305b80ee3a8c3e2bbc725f1d0d6838b0032cfa", + "export-binding-resolution/native-after-03.log": "2800a5ab5cdc73a643e4c53e62ad0e5f0442780627792ef2008a40e11fb5f7f1", + "export-binding-resolution/native-before-02.log": "a2bd56d4129191018cdd298d3df8f7f15c544dd7ecdae33777606aa5e38ebb9b", + "export-binding-resolution/native-before.log": "110c861b9e2a5806b5e9d4505af0dc66fdbc859d336d86e72eb503a9fbb17d35", + "export-binding-resolution/native-broad-01.log": "27a6976b0a8c97692f75940feb5fa80c0942aa0bfa94a0b39760fdc48b0d449e", + "export-binding-resolution/panel-a-01.log": "91ef624640fff89c04522ee0fd95fdd9ab0d35eba5d7e89ac54fdda4d5f9d55d", + "runs/export-binding-panel-a-01/run.json": "c96dfecd4fc47096673e7b1eb105e79fb8a3a631394e79be7be1227c0c1db8a4" + } +} diff --git a/crates/compass-model/src/document.rs b/crates/compass-model/src/document.rs index 8d1673689..5eb0d4262 100644 --- a/crates/compass-model/src/document.rs +++ b/crates/compass-model/src/document.rs @@ -959,7 +959,7 @@ impl GraphDocument { const QUERY_CACHE_MAGIC: &[u8; 8] = b"TRAILG01"; const AFFECTED_CACHE_MAGIC: &[u8; 8] = b"TRAILA02"; -const TRAVERSAL_CACHE_MAGIC: &[u8; 8] = b"TRAILT05"; +const TRAVERSAL_CACHE_MAGIC: &[u8; 8] = b"TRAILT06"; const QUERY_CACHE_HEADER_LEN: usize = 28; static QUERY_CACHE_SEQUENCE: AtomicU64 = AtomicU64::new(0); @@ -1012,6 +1012,7 @@ struct TraversalRawEdge { source_location: Option, relationship_site: Option, evidence_confidence: Option, + deferred: Option, } #[derive(Default)] @@ -1115,17 +1116,17 @@ impl<'de> Deserialize<'de> for TraversalRawEdge { "kind" => edge.kind = Some(map.next_value()?), "confidence" => edge.confidence = Some(map.next_value()?), "context" => edge.context = Some(map.next_value()?), + "deferred" => edge.deferred = Some(map.next_value()?), "source_file" => edge.source_file = Some(map.next_value()?), "source_location" => edge.source_location = Some(map.next_value()?), "relationshipSite" => edge.relationship_site = Some(map.next_value()?), "evidence" => { - let first_confidence = map - .next_value::()? - .0 - .into_iter() - .next() - .and_then(|evidence| evidence.confidence); - edge.evidence_confidence = first_confidence; + edge.evidence_confidence = weakest_traversal_confidence( + map.next_value::()? + .0 + .into_iter() + .map(|evidence| evidence.confidence.unwrap_or(Value::Null)), + ); } _ => { let _: IgnoredAny = map.next_value()?; @@ -1315,6 +1316,7 @@ struct TraversalCacheEdge( Option, Option, Option, + Option, ); impl TraversalRawGraphDocument { @@ -1429,20 +1431,10 @@ impl TraversalRawEdge { source_location, relationship_site, evidence_confidence, + deferred, } = self; - let confidence = confidence - .as_ref() - .and_then(value_as_python_string) - .map(Value::String) - .or_else(|| { - evidence_confidence.map(|value| match value.as_str() { - Some("exact") => Value::String("EXTRACTED".to_owned()), - Some("inferred") => Value::String("INFERRED".to_owned()), - Some("ambiguous") => Value::String("AMBIGUOUS".to_owned()), - Some("unresolved") => Value::String("UNRESOLVED".to_owned()), - Some(_) | None => value, - }) - }); + let confidence = + weakest_traversal_confidence(confidence.into_iter().chain(evidence_confidence)); TraversalCacheEdge( source, target, @@ -1471,10 +1463,25 @@ impl TraversalRawEdge { .and_then(Value::as_object) .and_then(source_anchor_location), ), + deferred, ) } } +// Traversal consumes the conservative confidence of the complete evidence set. +// Unknown or malformed confidence must not become an exact structural fact. +fn weakest_traversal_confidence(values: impl Iterator) -> Option { + values + .map(|value| match value.as_str() { + Some("exact" | "EXTRACTED") => "EXTRACTED", + Some("inferred" | "INFERRED") => "INFERRED", + Some("ambiguous" | "AMBIGUOUS") => "AMBIGUOUS", + _ => "UNRESOLVED", + }) + .max_by_key(|value| (confidence_rank(value), *value)) + .map(|value| Value::String(value.to_owned())) +} + fn projected_string_value(direct: Option<&Value>, fallback: Option) -> Option { direct .and_then(value_as_python_string) @@ -1539,6 +1546,7 @@ impl TraversalCacheDocument { context, source_file, source_location, + deferred, ) = edge; let mut attributes = Map::new(); insert_optional_value(&mut attributes, "relation", relation); @@ -1546,6 +1554,7 @@ impl TraversalCacheDocument { insert_optional_value(&mut attributes, "context", context); insert_optional_value(&mut attributes, "source_file", source_file); insert_optional_value(&mut attributes, "source_location", source_location); + insert_optional_value(&mut attributes, "deferred", deferred); EdgeRecord { source, target, diff --git a/crates/compass-model/tests/traversal_confidence.rs b/crates/compass-model/tests/traversal_confidence.rs new file mode 100644 index 000000000..09aef988d --- /dev/null +++ b/crates/compass-model/tests/traversal_confidence.rs @@ -0,0 +1,41 @@ +use std::error::Error; +use std::fs; + +use compass_model::GraphDocument; +use serde_json::json; + +#[test] +fn traversal_projection_preserves_weakest_confidence_and_deferred_state() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + fs::create_dir(directory.path().join("cache"))?; + let links = [ + json!({"source":"a", "target":"b", "kind":"exports", "evidence":[{"confidence":"exact"},{"confidence":"inferred"}]}), + json!({"source":"a", "target":"b", "kind":"exports", "evidence":[{"confidence":"inferred"},{"confidence":"exact"}]}), + json!({"source":"a", "target":"b", "kind":"exports", "deferred":true, "evidence":[{"confidence":"exact"}]}), + json!({"source":"a", "target":"b", "kind":"exports", "confidence":"EXTRACTED", "evidence":[{"confidence":"ambiguous"}]}), + ]; + fs::write( + &path, + serde_json::to_vec(&json!({"nodes":[{"id":"a"},{"id":"b"}],"links":links}))?, + )?; + for _ in 0..2 { + let graph = GraphDocument::load_for_traversal(&path)?; + assert_eq!(graph.links[0].string("confidence"), "INFERRED"); + assert_eq!(graph.links[1].string("confidence"), "INFERRED"); + assert_eq!(graph.links[2].boolean("deferred"), Some(true)); + assert_eq!(graph.links[3].string("confidence"), "AMBIGUOUS"); + } + // A cache bearing the previous format must be rebuilt, even when its + // filesystem signature still matches the authoritative graph. + let cache = directory.path().join("cache/graph.json.traversal-v1.cache"); + let mut bytes = fs::read(&cache)?; + bytes[..8].copy_from_slice(b"TRAILT05"); + fs::write(&cache, bytes)?; + let graph = GraphDocument::load_for_traversal(&path)?; + assert_eq!(graph.links[0].string("confidence"), "INFERRED"); + assert_eq!(graph.links[2].boolean("deferred"), Some(true)); + assert_eq!(&fs::read(cache)?[..8], b"TRAILT06"); + Ok(()) +} diff --git a/crates/compass-query/src/code_query.rs b/crates/compass-query/src/code_query.rs index 3c193959f..bcec1c653 100644 --- a/crates/compass-query/src/code_query.rs +++ b/crates/compass-query/src/code_query.rs @@ -3190,25 +3190,63 @@ impl CodeQueryEngine { } let normalized = normalize_symbol(query); let candidate_limit = usize::try_from(response.limits.max_candidates).unwrap_or(usize::MAX); - let (exact_nodes, exact_truncated) = self + let (mut exact_nodes, exact_truncated) = self .backend .nodes_by_normalized_name(&normalized, candidate_limit)?; instrumentation.work.candidates_read = instrumentation .work .candidates_read .saturating_add(u64::try_from(exact_nodes.len()).unwrap_or(u64::MAX)); + let mut proof_truncated = false; + if !exact_truncated + && exact_nodes.len() > 1 + && exact_nodes.len() <= crate::export_binding::MAX_EXPORT_PROOF_CANDIDATES + && exact_nodes.iter().any(|node| node.kind == NodeKind::Export) + { + use crate::export_binding::{ + BindingEdge, BindingNode, ProofRead, redundant_export_bindings, + }; + let candidates = exact_nodes + .iter() + .map(BindingNode::typed) + .collect::>(); + let pinned = self.backend.pin_discovery()?; + let proof = redundant_export_bindings(&candidates, |node, inbound, limit| { + let kinds = if inbound { + &[EdgeKind::Contains] + } else { + &[EdgeKind::Exports] + }; + let (edges, truncated, examined) = + pinned.matching_bounded_counted(node, inbound, kinds, true, limit)?; + Ok::<_, QueryError>(ProofRead { + edges: edges.iter().map(BindingEdge::typed).collect(), + truncated, + examined, + }) + })?; + instrumentation.work.edges_expanded = instrumentation + .work + .edges_expanded + .saturating_add(u64::try_from(proof.examined).unwrap_or(u64::MAX)); + proof_truncated = proof.truncated; + response.truncated |= proof_truncated; + exact_nodes.retain(|node| !proof.redundant.contains(&node.id)); + } let exact = exact_nodes .iter() .map(|node| node.id.clone()) .collect::>(); response.truncated |= exact_truncated; match exact.as_slice() { - [node] if !exact_truncated => return Ok(Some(node.clone())), + [node] if !exact_truncated && !proof_truncated => return Ok(Some(node.clone())), [] => {} _ => { response.diagnostics.push(QueryDiagnostic { code: QueryDiagnosticCode::AmbiguousMatch, - message: if exact_truncated { + message: if proof_truncated { + format!("Symbol {query:?} remains ambiguous because export evidence exceeded the {}-edge proof bound", crate::export_binding::MAX_EXPORT_PROOF_EDGES) + } else if exact_truncated { format!( "Symbol {query:?} exceeded the {}-candidate resolution bound", response.limits.max_candidates diff --git a/crates/compass-query/src/export_binding.rs b/crates/compass-query/src/export_binding.rs new file mode 100644 index 000000000..8282d0bea --- /dev/null +++ b/crates/compass-query/src/export_binding.rs @@ -0,0 +1,299 @@ +//! Name lookup may expose both a public export binding and its declaration. +//! Only explicit, coincident export evidence lets lookup remove the redundant +//! binding candidate. Exact-ID lookup bypasses this rule entirely. + +use std::collections::BTreeSet; + +use compass_model::code_graph::{EdgeRecord, NodeRecord}; +use compass_model::provenance::{EvidenceConfidence, SourceAnchor, effective_confidence}; + +pub(crate) const MAX_EXPORT_PROOF_EDGES: usize = 1024; +pub(crate) const MAX_EXPORT_PROOF_CANDIDATES: usize = 256; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) struct SourceRange { + file: String, + start: (u32, u32), + end: (u32, u32), +} + +impl SourceRange { + fn from_anchor(anchor: &SourceAnchor) -> Option { + Self::new( + anchor.file.clone(), + (anchor.start_line, anchor.start_column), + (anchor.end_line, anchor.end_column), + ) + } + + fn new(file: String, start: (u32, u32), end: (u32, u32)) -> Option { + (!file.is_empty() && start.0 > 0 && start < end).then_some(Self { file, start, end }) + } + + fn from_location(file: String, location: &str) -> Option { + let (start, end) = location.strip_prefix('L')?.split_once("-L")?; + let position = |value: &str| { + let (line, column) = value.split_once(':')?; + Some((line.parse::().ok()?, column.parse::().ok()?)) + }; + Self::new(file, position(start)?, position(end)?) + } +} + +pub(crate) struct BindingNode { + pub(crate) id: String, + kind: String, + name: String, + qualified_name: String, + site: Option, +} + +impl BindingNode { + pub(crate) fn typed(node: &NodeRecord) -> Self { + Self { + id: node.id.clone(), + kind: node.kind.as_str().to_owned(), + name: crate::normalize_code_query_symbol(&node.name), + qualified_name: node.qualified_name.clone(), + site: node.source.as_ref().and_then(SourceRange::from_anchor), + } + } + + pub(crate) fn legacy(node: &compass_model::NodeRecord) -> Self { + Self { + id: node.id.clone(), + kind: node.kind_name().to_owned(), + name: crate::normalize_code_query_symbol(node.label()), + qualified_name: node + .logical_property("qualified_name") + .and_then(|v| v.as_str().map(str::to_owned)) + .unwrap_or_default(), + site: SourceRange::from_location( + node.string("source_file"), + &node.string("source_location"), + ), + } + } + + fn represents(&self, target: &Self) -> bool { + self.kind == "export" + && !matches!( + target.kind.as_str(), + "export" | "import" | "module" | "file" + ) + && !self.qualified_name.is_empty() + && self.qualified_name == target.qualified_name + && self.name == target.name + && matches!((&self.site, &target.site), (Some(left), Some(right)) if left.file == right.file) + } +} + +pub(crate) struct BindingEdge { + source: String, + target: String, + site: Option, + exact: bool, +} + +impl BindingEdge { + pub(crate) fn typed(edge: &EdgeRecord) -> Self { + Self { + source: edge.source.clone(), + target: edge.target.clone(), + site: edge + .relationship_site + .as_ref() + .and_then(SourceRange::from_anchor), + exact: !edge.deferred + && effective_confidence(&edge.evidence) == Some(EvidenceConfidence::Exact), + } + } + + pub(crate) fn legacy(edge: &compass_model::EdgeRecord) -> Self { + // Do not inherit the permissive compatibility default for an unknown + // confidence spelling, or select the first item of mixed evidence. + let mut confidence_seen = false; + let mut exact = edge + .attributes + .get("deferred") + .is_none_or(|value| value.as_bool() == Some(false)); + if let Some(value) = edge.attributes.get("confidence") { + confidence_seen = true; + exact &= matches!(value.as_str(), Some("EXTRACTED" | "exact")); + } + if let Some(evidence) = edge.attributes.get("evidence") { + if let Some(items) = evidence.as_array() { + for item in items { + confidence_seen = true; + exact &= + item.get("confidence").and_then(serde_json::Value::as_str) == Some("exact"); + } + } else { + exact = false; + } + } + Self { + source: edge.source.clone(), + target: edge.target.clone(), + site: SourceRange::from_location( + edge.string("source_file"), + &edge.string("source_location"), + ), + exact: exact && confidence_seen, + } + } +} + +pub(crate) struct ProofRead { + pub(crate) edges: Vec, + pub(crate) examined: usize, + pub(crate) truncated: bool, +} + +#[derive(Default)] +pub(crate) struct ExportProof { + pub(crate) redundant: BTreeSet, + pub(crate) examined: usize, + pub(crate) truncated: bool, +} + +pub(crate) fn redundant_export_bindings( + candidates: &[BindingNode], + mut read: impl FnMut(&str, bool, usize) -> Result, +) -> Result { + let mut proof = ExportProof::default(); + if candidates.len() > MAX_EXPORT_PROOF_CANDIDATES { + return Ok(proof); + } + for binding in candidates.iter().filter(|n| n.kind == "export") { + if !candidates.iter().any(|target| binding.represents(target)) { + continue; + } + let Some(site) = binding.site.as_ref() else { + continue; + }; + let remaining = MAX_EXPORT_PROOF_EDGES.saturating_sub(proof.examined); + if remaining == 0 { + proof.truncated = true; + break; + } + let incoming = read(&binding.id, true, remaining)?; + proof.examined = proof.examined.saturating_add(incoming.examined); + if incoming.truncated { + proof.truncated = true; + break; + } + let [contains] = incoming.edges.as_slice() else { + continue; + }; + if contains.target != binding.id || !contains.exact || contains.site.as_ref() != Some(site) + { + continue; + } + let remaining = MAX_EXPORT_PROOF_EDGES.saturating_sub(proof.examined); + if remaining == 0 { + proof.truncated = true; + break; + } + let exports = read(&contains.source, false, remaining)?; + proof.examined = proof.examined.saturating_add(exports.examined); + if exports.truncated { + proof.truncated = true; + break; + } + // Missing occurrence evidence cannot rule out another target at this + // binding. Do not decide uniqueness from a partial source projection. + if exports.edges.iter().any(|edge| edge.site.is_none()) { + continue; + } + let selected = exports + .edges + .iter() + .filter(|edge| edge.site.as_ref() == Some(site)) + .collect::>(); + if selected.is_empty() + || selected + .iter() + .any(|edge| !edge.exact || edge.source != contains.source) + { + continue; + } + let targets = selected + .iter() + .map(|edge| edge.target.as_str()) + .collect::>(); + if targets.len() != 1 { + continue; + } + if candidates + .iter() + .any(|target| targets.contains(target.id.as_str()) && binding.represents(target)) + { + proof.redundant.insert(binding.id.clone()); + } + } + // Avoid a partially canonicalized result if any required proof exhausts + // its budget. The caller retains ambiguity, never an empty/no-match claim. + if proof.truncated { + proof.redundant.clear(); + } + Ok(proof) +} + +pub(crate) fn legacy_candidates( + graph: &compass_model::Graph, + mut matches: Vec, +) -> Vec { + if matches.len() < 2 + || matches.len() > MAX_EXPORT_PROOF_CANDIDATES + || !matches + .iter() + .any(|index| graph.node(*index).kind_name() == "export") + { + return matches; + } + let candidates = matches + .iter() + .map(|index| BindingNode::legacy(graph.node(*index))) + .collect::>(); + let result = + redundant_export_bindings::(&candidates, |id, inbound, limit| { + let Some(index) = graph.node_index(id) else { + return Ok(ProofRead { + edges: Vec::new(), + examined: 0, + truncated: false, + }); + }; + let mut edges = Vec::new(); + let mut examined = 0; + let mut truncated = false; + let indices: Box> = if inbound { + Box::new(graph.incoming_edges(index)) + } else { + Box::new(graph.outgoing_edges(index)) + }; + for edge_index in indices.take(limit.saturating_add(1)) { + examined += 1; + if examined > limit { + truncated = true; + break; + } + let edge = graph.edge(edge_index); + if edge.relation() == if inbound { "contains" } else { "exports" } { + edges.push(BindingEdge::legacy(edge)); + } + } + Ok(ProofRead { + edges, + examined, + truncated, + }) + }); + let proof = match result { + Ok(proof) => proof, + Err(never) => match never {}, + }; + matches.retain(|index| !proof.redundant.contains(&graph.node(*index).id)); + matches +} diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index ea93295a0..44501b9a2 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -9,6 +9,7 @@ mod code_query; mod cql; mod discovery; mod discovery_text; +mod export_binding; mod graph_engine; mod index; mod intent; diff --git a/crates/compass-query/src/score.rs b/crates/compass-query/src/score.rs index 9d82cde73..b4fc66673 100644 --- a/crates/compass-query/src/score.rs +++ b/crates/compass-query/src/score.rs @@ -511,7 +511,7 @@ pub fn find_exact_nodes(graph: &Graph, label: &str) -> Vec { return Vec::new(); } let norm_query = strip_diacritics(label).to_lowercase().trim().to_owned(); - graph + let matches = graph .nodes() .filter_map(|(index, node)| { let norm_label = normalized_label(node); @@ -528,7 +528,8 @@ pub fn find_exact_nodes(graph: &Graph, label: &str) -> Vec { || (!norm_qualified_name.is_empty() && norm_query == norm_qualified_name)) .then_some(index) }) - .collect() + .collect(); + crate::export_binding::legacy_candidates(graph, matches) } fn normalized_qualified_name(node: &NodeRecord) -> String { diff --git a/crates/compass-query/tests/export_binding_resolution.rs b/crates/compass-query/tests/export_binding_resolution.rs new file mode 100644 index 000000000..dd75eda75 --- /dev/null +++ b/crates/compass-query/tests/export_binding_resolution.rs @@ -0,0 +1,341 @@ +mod support; + +use std::error::Error; +use std::fs; +use std::path::Path; + +use compass_graph::GraphSnapshotBuilder; +use compass_model::Graph; +use compass_model::code_graph::{EdgeKind, EdgeRecord, GraphDocument, NodeKind}; +use compass_model::identity::edge_id; +use compass_model::provenance::{EvidenceConfidence, ResolutionCandidate}; +use compass_model::query_contract::{ + CallRequest, CodeQueryLimits, NodeTrailRequest, QueryDiagnosticCode, +}; +use compass_query::{ + EngineSelection, find_exact_nodes, open, open_with_engine, open_with_store, + render_shortest_path_with_limit, +}; +use compass_store::{STORE_FILE_NAME, STORE_REF_FILE_NAME, SqliteStore}; + +fn fixture(path: &Path) -> Result> { + support::write_graph(path)?; + fs::create_dir_all(path.parent().ok_or("missing graph parent")?.join("cache"))?; + let mut graph = GraphDocument::load(path)?; + let template = graph + .links + .first() + .cloned() + .ok_or("missing edge template")?; + graph.nodes = vec![ + support::node("module", NodeKind::Module, "api", "api"), + support::node("binding", NodeKind::Export, "entry", "api.entry"), + support::node("function", NodeKind::Function, "entry()", "api.entry"), + support::node("leaf", NodeKind::Function, "leaf()", "api.leaf"), + ]; + graph.links = vec![ + edge(&template, "module", EdgeKind::Contains, "binding"), + edge(&template, "module", EdgeKind::Exports, "function"), + edge(&template, "function", EdgeKind::Calls, "leaf"), + ]; + Ok(graph) +} + +fn edge(template: &EdgeRecord, source: &str, kind: EdgeKind, target: &str) -> EdgeRecord { + let mut edge = template.clone(); + edge.source = source.to_owned(); + edge.target = target.to_owned(); + edge.kind = kind; + edge.id = edge_id(source, kind, target, edge.relationship_site.as_ref(), None); + edge.key.clone_from(&edge.id); + edge +} + +fn ids(graph: &Graph, query: &str) -> Vec { + let mut ids = find_exact_nodes(graph, query) + .into_iter() + .map(|index| graph.node(index).id.clone()) + .collect::>(); + ids.sort(); + ids +} + +#[test] +fn coincident_export_binding_resolves_to_its_proven_declaration() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + let mut document = fixture(&path)?; + for reverse in [false, true] { + if reverse { + document.nodes.reverse(); + document.links.reverse(); + } + fs::write(&path, serde_json::to_vec(&document)?)?; + // Full document, cold traversal projection, and warm compact cache + // must retain enough evidence to make the same decision. + for projection in [ + document.to_legacy_document()?, + compass_model::GraphDocument::load_for_traversal(&path)?, + compass_model::GraphDocument::load_for_traversal(&path)?, + ] { + let graph = Graph::from_document(projection)?; + for query in ["entry", "entry()", "api.entry"] { + assert_eq!(ids(&graph, query), ["function"], "{query}"); + } + assert_eq!(ids(&graph, "binding"), ["binding"]); + let answer = render_shortest_path_with_limit(&graph, "entry", "leaf", 2)?; + assert!(answer.contains("1 hops"), "{answer}"); + assert!(!answer.contains("AMBIGUOUS"), "{answer}"); + } + let store = SqliteStore::open(directory.path().join(STORE_FILE_NAME))?; + let prepared = GraphSnapshotBuilder::new().prepare(&store, &document)?; + GraphSnapshotBuilder::new().activate(&store, &prepared)?; + fs::write( + directory.path().join(STORE_REF_FILE_NAME), + serde_json::to_vec(&store.snapshot_reference()?)?, + )?; + store.checkpoint()?; + for engine in [ + open_with_engine( + &path, + None, + &directory.path().join("cache"), + EngineSelection::Json, + )?, + open_with_engine( + &path, + None, + &directory.path().join("direct-cache"), + EngineSelection::Store, + )?, + open_with_store(&store, &path, None, &directory.path().join("store-cache"))?, + ] { + let response = engine.callees(CallRequest { + symbol: "entry".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits::default(), + })?; + assert!( + !response + .diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::AmbiguousMatch) + ); + assert!( + response + .edges + .iter() + .any(|e| e.source == "function" && e.target == "leaf") + ); + let trail = engine.node_trail(NodeTrailRequest { + source: "entry".to_owned(), + target: "leaf".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits::default(), + })?; + assert_eq!(trail.paths.len(), 1); + let exact = engine.callees(CallRequest { + symbol: "binding".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits::default(), + })?; + assert!(exact.edges.is_empty(), "exact binding ID must not redirect"); + } + } + Ok(()) +} + +#[test] +fn incomplete_or_conflicting_export_proof_preserves_ambiguity() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + for case in [ + "missing", + "wrong_site", + "reversed", + "inferred", + "mixed_confidence", + "deferred", + "missing_site", + "ambiguous", + "two_targets", + "other_declaration", + "wrong_qualified_name", + ] { + let mut document = fixture(&path)?; + match case { + "missing" => document.links.retain(|e| e.kind != EdgeKind::Exports), + "wrong_site" => { + let site = document.links[1] + .relationship_site + .as_mut() + .ok_or("missing site")?; + site.end_byte = 3; + site.end_column = 3; + } + "reversed" => { + document.links[1].source = "function".to_owned(); + document.links[1].target = "module".to_owned(); + } + "inferred" => document.links[1].evidence[0].confidence = EvidenceConfidence::Inferred, + "mixed_confidence" => { + let mut weaker = document.links[1].evidence[0].clone(); + weaker.confidence = EvidenceConfidence::Inferred; + document.links[1].evidence.push(weaker); + } + "deferred" => document.links[1].deferred = true, + "missing_site" => document.links[1].relationship_site = None, + "ambiguous" => { + let evidence = &mut document.links[1].evidence[0]; + evidence.confidence = EvidenceConfidence::Ambiguous; + evidence.candidates = ["function", "leaf"] + .map(|id| ResolutionCandidate { + node_id: id.to_owned(), + reason: "two possible targets".to_owned(), + confidence: EvidenceConfidence::Exact, + score: None, + anchor: None, + }) + .to_vec(); + } + "two_targets" => { + document.nodes.push(support::node( + "other", + NodeKind::Function, + "elsewhere", + "api.elsewhere", + )); + document.links.push(edge( + &document.links[1], + "module", + EdgeKind::Exports, + "other", + )); + } + "other_declaration" => document.nodes.push(support::node( + "other", + NodeKind::Function, + "entry()", + "other.entry", + )), + "wrong_qualified_name" => document.nodes[2].qualified_name = "other.entry".to_owned(), + _ => return Err("unexpected case".into()), + } + let graph = Graph::from_document(document.to_legacy_document()?)?; + assert!(ids(&graph, "entry").len() >= 2, "{case}"); + assert!( + render_shortest_path_with_limit(&graph, "entry", "leaf", 2).is_err(), + "{case}" + ); + // Graph validity is independent of proof. Mutation keys must follow + // updated endpoints/site; all cases remain valid public graphs. + for edge in &mut document.links { + edge.id = edge_id( + &edge.source, + edge.kind, + &edge.target, + edge.relationship_site.as_ref(), + None, + ); + edge.key.clone_from(&edge.id); + } + // Reverse exports is deliberately an invalid endpoint contract, so + // the permissive legacy reader alone exercises that negative. + if case == "reversed" { + continue; + } + fs::write(&path, serde_json::to_vec(&document)?)?; + for _ in 0..2 { + let graph = + Graph::from_document(compass_model::GraphDocument::load_for_traversal(&path)?)?; + assert!(ids(&graph, "entry").len() >= 2, "cached {case}"); + } + let engine = open(&path, None, &directory.path().join("negative-cache"))?; + let response = engine.callees(CallRequest { + symbol: "entry".to_owned(), + include_heuristic: true, + limits: CodeQueryLimits::default(), + })?; + assert!( + response + .diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::AmbiguousMatch), + "{case}" + ); + assert!(response.edges.is_empty(), "{case}"); + } + Ok(()) +} + +#[test] +fn truncated_candidate_or_export_evidence_never_selects_a_target() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + let mut document = fixture(&path)?; + fs::write(&path, serde_json::to_vec(&document)?)?; + let engine = open(&path, None, &directory.path().join("cache"))?; + let response = engine.callees(CallRequest { + symbol: "entry".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits { + max_candidates: 1, + ..CodeQueryLimits::default() + }, + })?; + assert!(response.truncated); + assert!( + response + .diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::AmbiguousMatch) + ); + assert!(response.edges.is_empty()); + for index in 0..1024 { + let id = format!("other:{index}"); + document.nodes.push(support::node( + &id, + NodeKind::Function, + "other", + &format!("api.other{index}"), + )); + document + .links + .push(edge(&document.links[1], "module", EdgeKind::Exports, &id)); + } + fs::write(&path, serde_json::to_vec(&document)?)?; + let graph = Graph::from_document(document.to_legacy_document()?)?; + assert_eq!(ids(&graph, "entry"), ["binding", "function"]); + let engine = open(&path, None, &directory.path().join("bounded-cache"))?; + let response = engine.callees(CallRequest { + symbol: "entry".to_owned(), + include_heuristic: false, + limits: CodeQueryLimits::default(), + })?; + assert!(response.truncated); + assert!(response.diagnostics.iter().any( + |d| d.code == QueryDiagnosticCode::AmbiguousMatch && d.message.contains("proof bound") + )); + assert!(response.edges.is_empty()); + Ok(()) +} + +#[test] +fn coarse_legacy_source_location_is_not_export_binding_proof() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + let document = fixture(&path)?; + let mut legacy = document.to_legacy_document()?; + let binding = legacy + .nodes + .iter_mut() + .find(|node| node.id == "binding") + .ok_or("missing binding")?; + binding.attributes.insert( + "source_location".to_owned(), + serde_json::Value::String("L1".to_owned()), + ); + let graph = Graph::from_document(legacy)?; + assert_eq!(ids(&graph, "entry"), ["binding", "function"]); + Ok(()) +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index d55d60079..10bcff0e6 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1565,13 +1565,70 @@ and topology checks, lifecycle determinism, Markdown checks, the release build, and frontend precedence/positive/negative checks. This pass qualifies Java production commit `22814e59`; it does not establish comparative superiority. +#### Export binding query resolution and traversal cache fidelity + +Redux's `miniKindOf` has separate export and function records. The graph already +contains its three reviewed local calls, but ordinary name lookup previously +refused the export/function pair as ambiguous. Lookup now removes a redundant +export candidate only when complete exact, nondeferred `contains` and `exports` +evidence proves one declaration at the binding occurrence. The declaration must +already be a candidate with the same normalized name, nonempty qualified name, +and source file. Genuine competing declarations remain ambiguous; exact export +IDs retain their original meaning. Proof is bounded to 256 candidates and 1,024 +examined edges plus one truncation probe. Exhaustion retains the original +candidates and reports incomplete typed resolution. + +A failed-before regression also exposed the compact traversal reader retaining +only the first evidence confidence and dropping `deferred`. It now retains the +weakest confidence across evidence and the compatibility field, plus deferred +state. Disposable traversal cache magic advances from `TRAILT05` to `TRAILT06`; +published graphs, identities, schemas, and AST cache semantics remain unchanged. + +Fresh paired extraction and all 110 unchanged requests on the same Go, Python, +Java, TypeScript, and Rust pins produce **byte-identical graphs for both tools** +relative to the Java checkpoint. The three newly passing text questions are +Redux callees and the two directions of the undirected `miniKindOf`/`ctorName` +path. Source review confirms the three local callees and the call occurrence; +the reverse traversal does not claim a reversed call. + +| Measure | Compass | Graphify | +| --- | ---: | ---: | +| Query text oracle | 49/55 | 46/55 | +| Source-checked paths | 8/10 | 8/10 | +| Selected source relationships | 17/21 | 20/21 | +| All reviewed occurrences, corrected Click oracle | 17/21 | 17/21 | + +The unchanged relationship oracle still cannot identify four Redux pairs +uniquely because export and function records remain distinct. These failures +are retained; they are neither four missing calls nor recovered extraction +relationships. Both WalkDir ordinary-name path witnesses still fail both tools: +Compass preserves genuine ambiguity, while Graphify returns an unreviewed +route. A separate post-output source census reviews 126 coincident Redux +bindings; this establishes static correspondence, not 126 successful public +queries or representative precision. The +[development review](../../benchmarks/agent_query/export_binding_development_review.json) +records source, executable, graph, oracle, and raw-artifact hashes. + +Verification: **1,180 native tests passed, zero failed, two ignored** across +workspace libraries/binaries and the selected export, cache, traversal, store, +CLI query, and product integration targets. The four export regressions include +direct SQLite, JSON, generic materialized store, cold/warm projection, preserved +ID/ambiguity behavior, and proof exhaustion. The cache regression includes +mixed-confidence order, deferred state, and stale cache rebuilding. Workspace +and selected-integration Clippy pass with warnings denied. A new full fixture +qualification is running; the previous Java pass does not qualify this change. +No MCP rerun, full-answer precision, community cohesion, god-object quality, +directed/long-path, latency, or general superiority claim follows. This reused +panel remains development evidence. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. The corrected fixture and selected real-source evidence above do not replace those broader checks. -2. Extend source-proven loop/result/iterator inference and TypeScript declaration - identity to recover the remaining fd and Redux misses. Address Java varargs +2. Extend source-proven loop/result/iterator inference to recover the remaining + fd misses. Evaluate TypeScript identity independently of the bounded query + binding proof above. Address Java varargs signature completeness and unresolved receiver forms with separate evidence. Keep exact build/source provenance for subsequent release comparisons; From bc4374a7c8161fc342cf12a6d1508bc7fb1f2994 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 21:31:42 -0700 Subject: [PATCH 34/97] eval: register source-reviewed directed path development questions --- ...irected_path_development_registration.json | 459 ++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 benchmarks/agent_query/directed_path_development_registration.json diff --git a/benchmarks/agent_query/directed_path_development_registration.json b/benchmarks/agent_query/directed_path_development_registration.json new file mode 100644 index 000000000..a3f0cf46b --- /dev/null +++ b/benchmarks/agent_query/directed_path_development_registration.json @@ -0,0 +1,459 @@ +{ + "schema": "compass.directed-path-development-registration/1", + "registeredAt": "2026-09-27T04:31:32.969188+00:00", + "scope": "Source-reviewed directed two-to-four-call paths across five existing development repositories. Registered before executing these new endpoint pairs. Prior graphs/outputs and fixes have been observed; not held-out evaluation or population precision.", + "baselineRun": "export-binding-panel-a-01", + "tools": [ + { + "binary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/export-binding-resolution/compass", + "binarySha256": "04eab05bd5bbc94f3f0f86580bdd3de9950599bc9e548b8f8ba95791b8baa0c9", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30" + }, + { + "binary": "/Users/haipingfu/.local/bin/graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67" + } + ], + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "source": "/Volumes/Workspace/Github/go-chi/chi", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/chi/compass/compass-out/snapshots/snapshot-1790482768478384000-36008-0/graph.json", + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/chi/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "source": "/Volumes/Workspace/Github/pallets/click", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/click/compass/compass-out/snapshots/snapshot-1790482790210688000-36710-0/graph.json", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/click/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "source": "/Volumes/Workspace/Github/jhy/jsoup", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/jsoup/compass/compass-out/snapshots/snapshot-1790482841846079000-37569-0/graph.json", + "compassGraphSha256": "d69152cc8900afc3f11756b8ce841adbed98ae6663fee4ebc64957974c8f860c", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/jsoup/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "source": "/Volumes/Workspace/Github/reduxjs/redux", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/redux/compass/compass-out/snapshots/snapshot-1790482928474073000-39900-0/graph.json", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/redux/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "source": "/Volumes/Workspace/Github/BurntSushi/walkdir", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/walkdir/compass/compass-out/snapshots/snapshot-1790482954351623000-40155-0/graph.json", + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/walkdir/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "protocol": { + "positiveRequests": 5, + "reverseDirectionProbes": 5, + "sourceEndpoints": "Use identical short source names for both tools. No post-output IDs, fuzzy candidate selection, or retry substitutions in the primary comparison.", + "compassArgv": [ + "compass", + "node", + "SOURCE", + "TARGET", + "--max-depth", + "8", + "--max-paths", + "1", + "--format", + "json", + "--graph", + "GRAPH" + ], + "graphifyArgv": [ + "graphify", + "path", + "SOURCE", + "TARGET", + "--directed", + "--graph", + "GRAPH" + ], + "deadlineSeconds": 60, + "captureLimitBytesPerStream": 16777216, + "graphs": "Reuse hash-verified fresh paired graphs. This measures query behavior; no fresh extraction or performance claim.", + "limits": "Compass has an explicit depth bound; Graphify path CLI exposes no corresponding depth flag in help. External deadline and capture bound apply equally; do not claim equal internal work.", + "positiveJudgment": "A complete directed call chain must identify the source declarations and each edge orientation/occurrence in the graph and source. Endpoint echoes, candidate lists, disconnected edges, or a reversed route do not count. Registered route is a positive witness, not a requirement to choose the same shortest route; independently source-review any alternate returned route.", + "reverseJudgment": "Diagnostic probes only. The forward source witness does not prove global reverse unreachability. No-path/ambiguous/limit answers receive no absence credit. Review any returned reverse route for actual directed source support.", + "ambiguity": "Refusal of genuine ambiguity is safe but does not complete a positive path request; retain in recall denominator and report resolution separately. Unwarned selection cannot establish identity when the rendered label collides.", + "confidence": "Source-supported inferred edges may be recorded as such; neither tool gets exact-evidence credit for weak/unknown confidence.", + "timing": "Concurrent fixture qualification; no latency or cost ranking.", + "fullAnswer": "Keep all outputs, failures, alternate-path judgments and rejected identities. No tool-output-only semantic gold." + }, + "witnesses": [ + { + "id": "chi-directed-chain", + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "source": "MethodFunc", + "target": "longestPrefix", + "nodes": [ + { + "file": "mux.go", + "line": 137, + "text": "func (mx *Mux) MethodFunc(", + "symbol": "MethodFunc", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + }, + { + "file": "mux.go", + "line": 127, + "text": "func (mx *Mux) Method(method,", + "symbol": "Method", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + }, + { + "file": "mux.go", + "line": 430, + "text": "func (mx *Mux) handle(method", + "symbol": "handle", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + }, + { + "file": "tree.go", + "line": 148, + "text": "func (n *node) InsertRoute(", + "symbol": "InsertRoute", + "fileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79" + }, + { + "file": "tree.go", + "line": 822, + "text": "func longestPrefix(", + "symbol": "longestPrefix", + "fileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79" + } + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "mux.go", + "line": 138, + "text": "mx.Method(method, pattern, handlerFn)", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "mux.go", + "line": 132, + "text": "mx.handle(m, pattern, handler)", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "mux.go", + "line": 450, + "text": "return mx.tree.InsertRoute(method, pattern, h)", + "fileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "tree.go", + "line": 201, + "text": "commonPrefix := longestPrefix(search, n.prefix)", + "fileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79" + } + } + ], + "sourceInterpretation": "Four static call steps from registering a method function to matching a radix prefix. Conditional traversal within InsertRoute; not a claim that every registration executes every branch." + }, + { + "id": "click-directed-chain", + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "source": "open_file", + "target": "_is_binary_reader", + "nodes": [ + { + "file": "src/click/utils.py", + "line": 381, + "text": "def open_file(", + "symbol": "open_file", + "fileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17" + }, + { + "file": "src/click/_compat.py", + "line": 374, + "text": "def open_stream(", + "symbol": "open_stream", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + { + "file": "src/click/_compat.py", + "line": 319, + "text": "def get_binary_stdin(", + "symbol": "get_binary_stdin", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + { + "file": "src/click/_compat.py", + "line": 176, + "text": "def _find_binary_reader(", + "symbol": "_find_binary_reader", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + { + "file": "src/click/_compat.py", + "line": 154, + "text": "def _is_binary_reader(", + "symbol": "_is_binary_reader", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + } + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)", + "fileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/click/_compat.py", + "line": 392, + "text": "return get_binary_stdin(), False", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/click/_compat.py", + "line": 320, + "text": "reader = _find_binary_reader(sys.stdin)", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/click/_compat.py", + "line": 181, + "text": "if _is_binary_reader(stream, False):", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + } + } + ], + "sourceInterpretation": "Four static calls on the non-lazy binary standard-input branch. The final helper has another occurrence in the same caller; this witness checks at least one supported occurrence, not occurrence completeness." + }, + { + "id": "jsoup-directed-chain", + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "source": "isValid", + "target": "copySafeNodes", + "nodes": [ + { + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 434, + "text": "public static boolean isValid(String", + "symbol": "isValid", + "fileSha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7" + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 124, + "text": "public boolean isValidBodyHtml(String", + "symbol": "isValidBodyHtml", + "fileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 182, + "text": "private int copySafeNodes(Element", + "symbol": "copySafeNodes", + "fileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + } + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/main/java/org/jsoup/Jsoup.java", + "line": 435, + "text": "return new Cleaner(safelist).isValidBodyHtml(bodyHtml);", + "fileSha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 131, + "text": "int numDiscarded = copySafeNodes(dirty.body(), clean.body());", + "fileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + } + } + ], + "sourceInterpretation": "Two source-proven calls. Constructor receiver is covered by the prior development fix; existing knowledge is disclosed, not fresh held-out confirmation." + }, + { + "id": "redux-directed-chain", + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "source": "kindOf", + "target": "ctorName", + "nodes": [ + { + "file": "src/utils/kindOf.ts", + "line": 62, + "text": "export function kindOf(", + "symbol": "kindOf", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + { + "file": "src/utils/kindOf.ts", + "line": 2, + "text": "export function miniKindOf(", + "symbol": "miniKindOf", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + { + "file": "src/utils/kindOf.ts", + "line": 40, + "text": "function ctorName(", + "symbol": "ctorName", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + } + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/utils/kindOf.ts", + "line": 66, + "text": "typeOfVal = miniKindOf(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + } + }, + { + "relation": "calls", + "direction": "forward", + "site": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + } + } + ], + "sourceInterpretation": "Two static calls conditional on development mode and a nonprimitive/non-Date/non-Error input. Export binding correction already informed by this file; no held-out claim." + }, + { + "id": "walkdir-directed-chain", + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "source": "handle_entry", + "target": "from_loop", + "nodes": [ + { + "file": "src/lib.rs", + "line": 840, + "text": " fn handle_entry(", + "symbol": "handle_entry", + "fileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a" + }, + { + "file": "src/lib.rs", + "line": 961, + "text": " fn follow(&self", + "symbol": "follow", + "fileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a" + }, + { + "file": "src/lib.rs", + "line": 973, + "text": " fn check_loop Date: Sat, 26 Sep 2026 21:37:01 -0700 Subject: [PATCH 35/97] eval: record directed call-path parity and unresolved failures --- .../directed_path_development_review.json | 743 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 51 +- 2 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 benchmarks/agent_query/directed_path_development_review.json diff --git a/benchmarks/agent_query/directed_path_development_review.json b/benchmarks/agent_query/directed_path_development_review.json new file mode 100644 index 000000000..51de059a8 --- /dev/null +++ b/benchmarks/agent_query/directed_path_development_review.json @@ -0,0 +1,743 @@ +{ + "schema": "compass.directed-path-development-review/1", + "scope": "Source-reviewed directed two-to-four-call paths across five existing development repositories. Registered before executing these new endpoint pairs. Prior graphs/outputs and fixes have been observed; not held-out evaluation or population precision.", + "registrationCommit": "bc4374a7c8161fc342cf12a6d1508bc7fb1f2994", + "registrationSha256": "749eaca9649b1975f099445b4eab7a17259b4578a01747aaa7182a622d0282e6", + "runSha256": "319e4cf5dbed87efa4b4119c7238fa77c610a2d52d9530782751f11969260b83", + "collectorSha256": "c9504a93daa4e18b4cab590ca8a35f6e5499bb15e4fa3b0fe5345a10dca79605", + "auditorSha256": "643f7dc1eee7329c8ebf54e6e71fcc60266392eb713179fb4336076a1a9a1d74", + "summary": { + "compass": { + "sourceSupportedCallPaths": 2, + "literalFrozenOccurrenceSites": 1, + "positiveRequests": 5 + }, + "graphify": { + "sourceSupportedCallPaths": 2, + "literalFrozenOccurrenceSites": 2, + "positiveRequests": 5 + } + }, + "results": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"MethodFunc\" matched 2 nodes", + "nodeId": null, + "path": null + } + ] + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "route length differs from reviewed witness", + "Returned route mixes references and method membership; it is not a directed call chain." + ], + "nodes": [], + "steps": [] + }, + { + "question": "click-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [], + "nodes": [ + { + "id": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "source": { + "file": "src/click/utils.py", + "startByte": 11486, + "endByte": 13132, + "startLine": 381, + "startColumn": 0, + "endLine": 427, + "endColumn": 12 + }, + "name": "open_file()" + }, + { + "id": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "source": { + "file": "src/click/_compat.py", + "startByte": 11423, + "endByte": 14244, + "startLine": 374, + "startColumn": 0, + "endLine": 452, + "endColumn": 40 + }, + "name": "open_stream()" + }, + { + "id": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "source": { + "file": "src/click/_compat.py", + "startByte": 9593, + "endByte": 9800, + "startLine": 319, + "startColumn": 0, + "endLine": 323, + "endColumn": 17 + }, + "name": "get_binary_stdin()" + }, + { + "id": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "source": { + "file": "src/click/_compat.py", + "startByte": 4815, + "endByte": 5484, + "startLine": 176, + "startColumn": 0, + "endLine": 191, + "endColumn": 15 + }, + "name": "_find_binary_reader()" + }, + { + "id": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "source": { + "file": "src/click/_compat.py", + "startByte": 4230, + "endByte": 4529, + "startLine": 154, + "startColumn": 0, + "endLine": 160, + "endColumn": 55 + }, + "name": "_is_binary_reader()" + } + ], + "steps": [ + { + "edgeId": "sha256:b4f41add3eb26a6b6699864bed02e49dd24595a669501b1f2fbd44140601556e", + "source": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "target": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "kind": "calls", + "site": { + "file": "src/click/utils.py", + "startByte": 12980, + "endByte": 12991, + "startLine": 422, + "startColumn": 22, + "endLine": 422, + "endColumn": 33 + }, + "sourceBytes": "open_stream", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:b3d1f4a591b7a358eb841388e97117bbd4a2377817aa4014c2b9f5fed9759bc5", + "source": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "target": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 12085, + "endByte": 12101, + "startLine": 392, + "startColumn": 19, + "endLine": 392, + "endColumn": 35 + }, + "sourceBytes": "get_binary_stdin", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:ab477fcd547ac3d833934602fc1d5871a98378031c63dc125aeaebbf5e84cb34", + "source": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "target": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 9644, + "endByte": 9663, + "startLine": 320, + "startColumn": 13, + "endLine": 320, + "endColumn": 32 + }, + "sourceBytes": "_find_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:0ed10b05deb50320b392d13c0e611c3732abfc5d77e1267e3bb744afc3596349", + "source": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "target": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 5399, + "endByte": 5416, + "startLine": 188, + "startColumn": 27, + "endLine": 188, + "endColumn": 44 + }, + "sourceBytes": "_is_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": false, + "acceptedAlternateOccurrences": [ + { + "file": "src/click/_compat.py", + "line": 188, + "text": "if buf is not None and _is_binary_reader(buf, True):", + "reason": "Same reviewed caller and callee, second direct call occurrence. Source window was read before execution; selected alternate explicitly adjudicated after output. Frozen site at line 181 remains unchanged." + } + ] + } + ], + "responseDiagnostics": [] + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "diagnostics": [], + "nodes": [ + "src_click_utils_open_file", + "src_click_compat_open_stream", + "src_click_compat_get_binary_stdin", + "src_click_compat_find_binary_reader", + "src_click_compat_is_binary_reader" + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)", + "fileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 392, + "text": "return get_binary_stdin(), False", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 320, + "text": "reader = _find_binary_reader(sys.stdin)", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 181, + "text": "if _is_binary_reader(stream, False):", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + } + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"isValid\" matched 2 nodes", + "nodeId": null, + "path": null + }, + { + "code": "incomplete_coverage", + "message": "Published graph coverage is incomplete: partial graph published after quarantining 0 nodes and 2 edges with 0 identity collisions; 0 examples omitted by the diagnostic cap", + "nodeId": null, + "path": null + } + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "expected exactly one rendered path header" + ], + "nodes": [], + "steps": [] + }, + { + "question": "redux-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"kindOf\" matched 5 nodes", + "nodeId": null, + "path": null + }, + { + "code": "incomplete_coverage", + "message": "Published graph coverage is incomplete: partial graph published after quarantining 0 nodes and 2 edges with 0 identity collisions; 0 examples omitted by the diagnostic cap", + "nodeId": null, + "path": null + } + ] + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "diagnostics": [], + "nodes": [ + "src_utils_kindof_kindof", + "src_utils_kindof_minikindof", + "src_utils_kindof_ctorname" + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 66, + "text": "typeOfVal = miniKindOf(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + } + ] + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "diagnostics": [], + "nodes": [ + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "source": { + "file": "src/lib.rs", + "startByte": 29143, + "endByte": 30869, + "startLine": 840, + "startColumn": 4, + "endLine": 882, + "endColumn": 5 + }, + "name": ".handle_entry()" + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "source": { + "file": "src/lib.rs", + "startByte": 34443, + "endByte": 34874, + "startLine": 961, + "startColumn": 4, + "endLine": 971, + "endColumn": 5 + }, + "name": ".follow()" + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "source": { + "file": "src/lib.rs", + "startByte": 34880, + "endByte": 35480, + "startLine": 973, + "startColumn": 4, + "endLine": 989, + "endColumn": 5 + }, + "name": ".check_loop()" + }, + { + "id": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "source": { + "file": "src/error.rs", + "startByte": 6769, + "endByte": 7076, + "startLine": 184, + "startColumn": 4, + "endLine": 196, + "endColumn": 5 + }, + "name": ".from_loop()" + } + ], + "steps": [ + { + "edgeId": "sha256:db51772e6e2a0f689b69f1447740b96e340679a37df7c50bfc727ffe85a06cf4", + "source": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "target": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 29337, + "endByte": 29348, + "startLine": 845, + "startColumn": 25, + "endLine": 845, + "endColumn": 36 + }, + "sourceBytes": "self.follow", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:9d2cbc7e60565250bba8045728a1451893838b7f1b8d26084a542957c88557c3", + "source": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "target": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 34811, + "endByte": 34826, + "startLine": 968, + "startColumn": 12, + "endLine": 968, + "endColumn": 27 + }, + "sourceBytes": "self.check_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:32f0ce77ab676891029891f1d836f95f3e4c8458dcee2b5d51f7128b8ad32bcf", + "source": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "target": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 35294, + "endByte": 35310, + "startLine": 981, + "startColumn": 27, + "endLine": 981, + "endColumn": 43 + }, + "sourceBytes": "Error::from_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + } + ], + "responseDiagnostics": [] + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "diagnostics": [ + "route length differs from reviewed witness", + "Returned route mixes references and method membership; it is not a directed call chain." + ], + "nodes": [], + "steps": [] + } + ], + "reverseProbes": { + "compass": { + "ambiguous": 3, + "boundedDirectionMismatch": 2, + "pathsReturned": 0 + }, + "graphify": { + "noDirectedPathReported": 5, + "pathsReturned": 0 + }, + "judgment": "No global unreachability/absence credit. Source witnesses support forward chains only; incomplete graph coverage and bounds remain." + }, + "limits": [ + "Selected short-name development requests, not held-out or representative precision.", + "One Compass Click path selects the second source-supported call at line 188 rather than the frozen site at 181; preserve both literal-site agreement and explicit alternate-occurrence adjudication.", + "Compass ambiguity refusals are safe but remain failures to complete these positive path requests.", + "Graphify Chi and WalkDir return mixed-relation graph routes rather than requested call chains. This does not by itself prove those structural relationships false.", + "Graphify jsoup returns no route; no success inferred from process exit 0.", + "Queries reused hash-verified graphs and overlapped fixture qualification; no extraction or timing ranking.", + "God-object responsibilities, community quality, broad precision and overall superiority remain unproven." + ], + "artifactBase": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/directed-path-development", + "tools": [ + { + "binary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/export-binding-resolution/compass", + "binarySha256": "04eab05bd5bbc94f3f0f86580bdd3de9950599bc9e548b8f8ba95791b8baa0c9", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30" + }, + { + "binary": "/Users/haipingfu/.local/bin/graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67" + } + ], + "graphs": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/chi/compass/compass-out/snapshots/snapshot-1790482768478384000-36008-0/graph.json", + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/chi/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/click/compass/compass-out/snapshots/snapshot-1790482790210688000-36710-0/graph.json", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/click/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/jsoup/compass/compass-out/snapshots/snapshot-1790482841846079000-37569-0/graph.json", + "compassGraphSha256": "d69152cc8900afc3f11756b8ce841adbed98ae6663fee4ebc64957974c8f860c", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/jsoup/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/redux/compass/compass-out/snapshots/snapshot-1790482928474073000-39900-0/graph.json", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/redux/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/walkdir/compass/compass-out/snapshots/snapshot-1790482954351623000-40155-0/graph.json", + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/runs/export-binding-panel-a-01/artifacts/walkdir/graphify/graphify-out/graph.json", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "artifactSha256": { + "audit.py": "643f7dc1eee7329c8ebf54e6e71fcc60266392eb713179fb4336076a1a9a1d74", + "auditor-correction.txt": "e961f81bc87f3c90c6aa09198f995edbc7e768f7fa71ad672bee1e1e0c4f237f", + "collect.log": "0dde3a629ee81c6f08e2236b7dd501101df56420709d8d803ef1659af3859077", + "collect.py": "c9504a93daa4e18b4cab590ca8a35f6e5499bb15e4fa3b0fe5345a10dca79605", + "raw/chi-directed-chain.forward.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/chi-directed-chain.forward.compass.stdout": "59ef2c10741f173a87455c8965b1776927f63f5580a27c7158600bcf7b7125a1", + "raw/chi-directed-chain.forward.graphify.stderr": "b9679c2a8cf75d4cc437463b970b844578b9bc39856f27321544066d264b8547", + "raw/chi-directed-chain.forward.graphify.stdout": "8edef8069e12a02a1030474a8c30010ca21b329e68b1b1473b9d6188fd4e01c8", + "raw/chi-directed-chain.reverse.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/chi-directed-chain.reverse.compass.stdout": "59ef2c10741f173a87455c8965b1776927f63f5580a27c7158600bcf7b7125a1", + "raw/chi-directed-chain.reverse.graphify.stderr": "54480bb6ec43a2bd3bf6cd010d2581331d3571cb475b8a1459a52a54e19d921c", + "raw/chi-directed-chain.reverse.graphify.stdout": "bf55b319e6afcbb676ac9162af11f385f2de0dbdee03a41ffbb0603deb839273", + "raw/click-directed-chain.forward.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/click-directed-chain.forward.compass.stdout": "dda553f1f6b9def88d52525abd02700e133855379236f516c945d95e2083c9f7", + "raw/click-directed-chain.forward.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/click-directed-chain.forward.graphify.stdout": "09dbe2d14d81374e08ee0dfa17a74bea50af43d82a178c03707932a6d3cc23ac", + "raw/click-directed-chain.reverse.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/click-directed-chain.reverse.compass.stdout": "ff7a885fde7acd12e5d98961662a6cc5cbbaed790b6ed0722559f81393b067d1", + "raw/click-directed-chain.reverse.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/click-directed-chain.reverse.graphify.stdout": "50841c22539e5ae7ffcfca37dbdb33e42f4f35cd6de99078661a70aefa6c746a", + "raw/jsoup-directed-chain.forward.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/jsoup-directed-chain.forward.compass.stdout": "081a1fcf19bc562c2adfd6d204ac91c6548e1d41aa7f966d88f4cfde5fc6feab", + "raw/jsoup-directed-chain.forward.graphify.stderr": "ad8b759ce936bd906bee37c4c7bf7f8c8bb7edd4473f01eaff2c5e71df608d9b", + "raw/jsoup-directed-chain.forward.graphify.stdout": "f196019e0433e931b43c8c17c497546df25309a5c0152cbef16e758bc28ffad8", + "raw/jsoup-directed-chain.reverse.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/jsoup-directed-chain.reverse.compass.stdout": "081a1fcf19bc562c2adfd6d204ac91c6548e1d41aa7f966d88f4cfde5fc6feab", + "raw/jsoup-directed-chain.reverse.graphify.stderr": "92f295833bb3dba142423fd2108b1e6b8dc244633f1de2b829818dd37ee9ca94", + "raw/jsoup-directed-chain.reverse.graphify.stdout": "0751dfab571e5ea79afb8511314fd1aab75bc07edc663306de6df401fef38f43", + "raw/redux-directed-chain.forward.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/redux-directed-chain.forward.compass.stdout": "fc4eaeb2e995696265d896adbdcc842836b63eadd7c6ce90d1d57791095192a8", + "raw/redux-directed-chain.forward.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/redux-directed-chain.forward.graphify.stdout": "64ed32a81d99e21609574da269fa2c7251bd772cc1ab42a5bf03f5041434d10d", + "raw/redux-directed-chain.reverse.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/redux-directed-chain.reverse.compass.stdout": "fc4eaeb2e995696265d896adbdcc842836b63eadd7c6ce90d1d57791095192a8", + "raw/redux-directed-chain.reverse.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/redux-directed-chain.reverse.graphify.stdout": "1b38644d467dabd82e3d08b865a373dfcb7fe718bcd7508cb24edab638a2d38f", + "raw/walkdir-directed-chain.forward.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/walkdir-directed-chain.forward.compass.stdout": "26dbb696599bb3b5f0e320a57166cc484c041eaabeece8083296f6468c89d38c", + "raw/walkdir-directed-chain.forward.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/walkdir-directed-chain.forward.graphify.stdout": "ff10254f943d3c2674a31b46397ebe8129a167ad45fad18ba713e948db5dff95", + "raw/walkdir-directed-chain.reverse.compass.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "raw/walkdir-directed-chain.reverse.compass.stdout": "0e2a5a7d8694d862c3c06c5b8dda8a61218376583b3a476d1a0ec40e6894d401", + "raw/walkdir-directed-chain.reverse.graphify.stderr": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "raw/walkdir-directed-chain.reverse.graphify.stdout": "9fa47e30f54e7b6fec501b25cca5cfdec2a28ad832fac0c3033616dd7c4eea73", + "registration.json": "749eaca9649b1975f099445b4eab7a17259b4578a01747aaa7182a622d0282e6", + "run.json": "319e4cf5dbed87efa4b4119c7238fa77c610a2d52d9530782751f11969260b83" + }, + "execution": { + "requests": 20, + "nonzeroExits": 0, + "timeouts": 0, + "outputLimits": 0 + }, + "reverseObservations": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "diagnostics": [ + "ambiguous_match" + ], + "truncated": false, + "paths": 0 + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "message": "No directed path found between 'longestPrefix' and 'MethodFunc'. Re-run with --undirected to search ignoring edge direction.", + "paths": 0 + }, + { + "question": "click-directed-chain", + "tool": "compass", + "diagnostics": [ + "direction_mismatch" + ], + "truncated": false, + "paths": 0 + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "message": "No directed path found between '_is_binary_reader' and 'open_file'. Re-run with --undirected to search ignoring edge direction.", + "paths": 0 + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "diagnostics": [ + "ambiguous_match", + "incomplete_coverage" + ], + "truncated": false, + "paths": 0 + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "message": "No directed path found between 'copySafeNodes' and 'isValid'. Re-run with --undirected to search ignoring edge direction.", + "paths": 0 + }, + { + "question": "redux-directed-chain", + "tool": "compass", + "diagnostics": [ + "ambiguous_match", + "incomplete_coverage" + ], + "truncated": false, + "paths": 0 + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "message": "No directed path found between 'ctorName' and 'kindOf'. Re-run with --undirected to search ignoring edge direction.", + "paths": 0 + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "diagnostics": [ + "direction_mismatch" + ], + "truncated": false, + "paths": 0 + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "message": "No directed path found between 'from_loop' and 'handle_entry'. Re-run with --undirected to search ignoring edge direction.", + "paths": 0 + } + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 10bcff0e6..f7489ff64 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -12,7 +12,7 @@ finding. A focused text-recall score cannot establish all of those properties. | Accurate code graph | Reviewed declaration and relationship precision/recall, direction, occurrences, unresolved/ambiguous cases | Source-first fd pair/occurrence audit added; receiver-shadowing correction has native regressions; fd loop recall remains open | | Better query answers | Held-out equivalent questions, independent source judgments, precision and recall | Five-repository development suites plus a separately selected source-first fd sample; neither establishes representative accuracy | | Better explanations | Correct target, source provenance, callers/callees and explicit uncertainty | Fresh paired fd answers expose a Compass callees miss and a Graphify wrong-owner edge hidden by the text oracle | -| Better navigation and walks | Valid ordered edges, direction, hop bounds, alternatives, ambiguity and negative cases | Existing path tests/suites are useful but do not prove real-repository path precision | +| Better navigation and walks | Valid ordered edges, direction, hop bounds, alternatives, ambiguity and negative cases | Five registered real-source directed call chains yield 2/5 source-supported answers per tool; ambiguity, relationship restrictions and broader path quality remain open | | Fair efficiency comparison | Same successful questions, repeated timings, token methodology and complete environment provenance | Paired token aggregation exists; bytes/4 remains an estimate | “God mode” is interpreted here as the existing `god_nodes` hub analysis. @@ -1621,6 +1621,55 @@ No MCP rerun, full-answer precision, community cohesion, god-object quality, directed/long-path, latency, or general superiority claim follows. This reused panel remains development evidence. +#### Directed call-path development comparison + +[Five source-reviewed chains](../../benchmarks/agent_query/directed_path_development_registration.json) +were committed as `bc4374a7` before their first execution: Chi registration to +radix-prefix matching (four calls), Click file opening to binary-reader testing +(four), jsoup validation to safe-node copying (two), Redux kind detection to +constructor-name inspection (two), and WalkDir entry handling to loop-error +creation (three). Both tools receive identical short endpoint names and the +same verified stored graphs. Compass uses `node` with depth eight and one path; +Graphify uses `path --directed`. Each command has the same external 60-second +deadline and 16 MiB capture ceiling. Their internal work limits differ. + +All 20 requests completed: five positive requests and five reverse-direction +probes per tool. Positive **source-supported directed call chains score 2/5 +for each tool**, with different failures: + +| Repository | Compass | Graphify | +| --- | --- | --- | +| Chi | Refuses interface/implementation name ambiguity | Returns a five-hop mixed-reference/method route, not a call chain | +| Click | Returns the four-call source chain | Returns the four-call source chain | +| jsoup | Refuses two genuine `isValid` declarations | Reports no directed route | +| Redux | Refuses function/import/module name ambiguity | Returns the two-call source chain | +| WalkDir | Returns the three-call source chain | Returns a seven-hop mixed-reference/method route, not a call chain | + +The Compass Click path selects the second `_is_binary_reader` occurrence at +line 188, while the frozen witness names line 181. Both occurrences were visible +in the source window reviewed before execution. The alternate is explicitly +adjudicated after output; the original witness is unchanged. Literal frozen +occurrence-site agreement is **Compass 1/5, Graphify 2/5**. The separate 2/5 +source-supported figure accepts this valid occurrence under the registered +policy and does not claim complete occurrence recall. + +Compass's ambiguity refusals are safe but do not complete the requested positive +paths. Graphify's mixed routes do not satisfy a call-chain request; this alone +does not establish that every structural relationship in those routes is false. +Neither tool returns a reverse path. Compass reports three ambiguities and two +bounded direction mismatches; Graphify reports five missing directed paths. +These receive **no global unreachability credit**. Forward source witnesses +cannot prove global reverse absence. + +The [review](../../benchmarks/agent_query/directed_path_development_review.json) +retains the executable/graph/registration hashes, raw outputs, identities, +source spans, alternate-occurrence adjudication, and all failures. Its first +audit attempt stopped because it assumed Rust occurrences covered only the +terminal method name; the corrected check requires the exact qualified source +expressions. No graph, request, or witness changed. This is selected development +evidence on reused graphs, with concurrent fixture qualification and no speed +claim. It establishes no directed-navigation lead. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From fc066af64867608e0838897f82e557c8e5f1c19f Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 21:42:41 -0700 Subject: [PATCH 36/97] docs: record passing export query fixture qualification --- .../export_binding_development_review.json | 12 ++++++++++-- .../code-graph-intelligence-audit-2026-09-26.md | 8 ++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/benchmarks/agent_query/export_binding_development_review.json b/benchmarks/agent_query/export_binding_development_review.json index f4e81537d..09c3c6d98 100644 --- a/benchmarks/agent_query/export_binding_development_review.json +++ b/benchmarks/agent_query/export_binding_development_review.json @@ -188,7 +188,14 @@ "coarse source location", "weakest mixed confidence and deferred state" ], - "fullFixtureQualification": "Running separately; previous Java full pass does not qualify this query/cache change." + "fullFixtureQualification": { + "status": "passed", + "exitCode": 0, + "productionCommit": "9b63873a", + "scope": "Native scale ceilings, semantic/topology assertions, deterministic lifecycle builds, Markdown, release build and frontend precedence/positive/negative/source-anchor checks.", + "logSha256": "08d1d2312cbe4860772a9e7ebf03de41e4c257a03a4ec366cde5ebf2cb93dad4", + "releaseExecutableSha256": "a073e8599c83f14fa21e37359ff3c183540c36adce7b7bbdea4823c9f031c322" + } }, "remainingLimits": [ "WalkDir ordinary-name path witnesses fail both tools; Compass retains genuine ambiguity and Graphify returns an unreviewed route.", @@ -227,6 +234,7 @@ "export-binding-resolution/native-before.log": "110c861b9e2a5806b5e9d4505af0dc66fdbc859d336d86e72eb503a9fbb17d35", "export-binding-resolution/native-broad-01.log": "27a6976b0a8c97692f75940feb5fa80c0942aa0bfa94a0b39760fdc48b0d449e", "export-binding-resolution/panel-a-01.log": "91ef624640fff89c04522ee0fd95fdd9ab0d35eba5d7e89ac54fdda4d5f9d55d", - "runs/export-binding-panel-a-01/run.json": "c96dfecd4fc47096673e7b1eb105e79fb8a3a631394e79be7be1227c0c1db8a4" + "runs/export-binding-panel-a-01/run.json": "c96dfecd4fc47096673e7b1eb105e79fb8a3a631394e79be7be1227c0c1db8a4", + "export-binding-resolution/full-qualification-01.log": "08d1d2312cbe4860772a9e7ebf03de41e4c257a03a4ec366cde5ebf2cb93dad4" } } diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index f7489ff64..647e5d3d8 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1615,8 +1615,12 @@ CLI query, and product integration targets. The four export regressions include direct SQLite, JSON, generic materialized store, cold/warm projection, preserved ID/ambiguity behavior, and proof exhaustion. The cache regression includes mixed-confidence order, deferred state, and stale cache rebuilding. Workspace -and selected-integration Clippy pass with warnings denied. A new full fixture -qualification is running; the previous Java pass does not qualify this change. +and selected-integration Clippy pass with warnings denied. Full fixture +qualification for production commit `9b63873a` completed with exit zero, +including native scale ceilings, semantic/topology checks, lifecycle +determinism, Markdown, release compilation, and frontend precedence, +positive/negative, and independent source-anchor checks. The release binary +and qualification log hashes are recorded in the development review. No MCP rerun, full-answer precision, community cohesion, god-object quality, directed/long-path, latency, or general superiority claim follows. This reused panel remains development evidence. From 44ef68e804e5874fc84a5f1548b91ca7414f68ee Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 22:01:22 -0700 Subject: [PATCH 37/97] fix: correct query continuation evaluation and limit help --- CHANGELOG.md | 4 + benchmarks/agent_query/README.md | 8 + .../cursor_harness_correction_review.json | 151 ++++++++++++++++ .../fixtures/java_varargs/VarargsDemo.java | 14 ++ .../java_varargs_diagnostic_review.json | 162 ++++++++++++++++++ benchmarks/agent_query/runner.py | 12 +- benchmarks/agent_query/tests/test_runner.py | 47 +++++ crates/compass-cli/src/help.rs | 8 +- crates/compass-cli/tests/help_cli.rs | 32 ++++ ...ode-graph-intelligence-audit-2026-09-26.md | 73 ++++++++ 10 files changed, 503 insertions(+), 8 deletions(-) create mode 100644 benchmarks/agent_query/cursor_harness_correction_review.json create mode 100644 benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java create mode 100644 benchmarks/agent_query/java_varargs_diagnostic_review.json diff --git a/CHANGELOG.md b/CHANGELOG.md index a3dcb6049..18eae11e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Correct natural discovery help to report the 10,000 examined-relationship + default independently of the 128 returned-edge default. Runtime limits are + unchanged. + - Resolve coincident export-binding and declaration name matches through exact export evidence, improving symbol-based paths and relationship queries while preserving genuine ambiguity and exact-ID selection. Traversal caches retain diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 9ef8984f5..c7c7c32de 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -26,6 +26,14 @@ closest documented operation on each side, and continuations are each tool's own Graphify because Compass `path` searches relationships in both directions. A row that a tool cannot answer fails and is reported as a recall gap. +Compass continuation requires one `Pagination:` footer with a nonterminal, +previously unseen cursor. `next=none` ends pagination; `next=` text in source +excerpts or prose does not authorize a follow-up. Multiple footers or a repeated +cursor stop continuation while preserving the unmet answer requirements. +Both LF and CRLF output are supported. Query-only replays must use each pinned +source checkout as their working directory, matching the full runner, so +digest-verified source excerpts remain available. + Name-resolution rows (`ambiguity`, `negative`) use Graphify's `explain`, the command that reports its candidate list for an ambiguous name and its explicit no-match, rather than `query`, which traverses the neighbourhood of a single diff --git a/benchmarks/agent_query/cursor_harness_correction_review.json b/benchmarks/agent_query/cursor_harness_correction_review.json new file mode 100644 index 000000000..9f5adfb80 --- /dev/null +++ b/benchmarks/agent_query/cursor_harness_correction_review.json @@ -0,0 +1,151 @@ +{ + "schema": "compass.cursor-harness-correction-review/1", + "scope": "Benchmark workflow correction only; no product recall or extraction improvement. Query-only replay of all 110 unchanged requests on verified retained graphs and pinned source working directories.", + "baselineRunId": "export-binding-panel-a-01", + "baselineRunnerDigest": "173242988699eb6fabd175c293634d3cf37c1394f76429540fd7af2a48ef7026", + "replayedRunnerDigest": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "finalRunnerDigest": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "suiteDigest": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "tools": [ + { + "name": "compass", + "version": "compass 0.3.30", + "binarySha256": "04eab05bd5bbc94f3f0f86580bdd3de9950599bc9e548b8f8ba95791b8baa0c9" + }, + { + "name": "graphify", + "version": "graphify 0.9.67", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3" + } + ], + "scores": { + "compass": { + "passed": 49, + "total": 55 + }, + "graphify": { + "passed": 46, + "total": 55 + } + }, + "changes": [ + { + "question": "redux-holdout-broad", + "tool": "compass", + "before": { + "exit": 1, + "followUps": 1, + "passed": false + }, + "after": { + "exit": 0, + "followUps": 0, + "passed": false + }, + "remainingFailure": "missing 'miniKindOf'" + } + ], + "rawCaptureAgreement": { + "files": 228, + "byteIdenticalToBaseline": 225, + "note": "All retained Compass captures match byte-for-byte. Three Graphify query outputs change edge ordering/content within their output budget, without changing observed scores or token counts. Do not attribute these changes to the Compass-only cursor fix or claim whole-answer equivalence. The invalid terminal-cursor request is not issued.", + "changedCaptures": [ + { + "path": "click/click-holdout-ask-callers.graphify.0.stdout", + "sha256": "35049e3d323057590a898a5e09d5576a0cd98f43cf5908eb515a0d01624d5135", + "baselineSha256": "1ca4304e3da5eed6d82203fd6d5e6f4e98f411e34e4110c28aa2bb2a61a1a4f3", + "identicalToBaseline": false + }, + { + "path": "jsoup/jsoup-holdout-ask-callers.graphify.0.stdout", + "sha256": "afd7f9557d93dec2219aa29e8eb5e0e67a1c71e36c1d1b5dc41a3e0f68748626", + "baselineSha256": "de5fcd3ed8c6df698b140e12b68959411d1878921410102d7c7e88eb646ba371", + "identicalToBaseline": false + }, + { + "path": "walkdir/walkdir-holdout-ask-callers.graphify.0.stdout", + "sha256": "8f75b32eed711ba8f2e1760be419d702831e21e9d178283bb03f05e869688971", + "baselineSha256": "8901bc64e4aaafdeda75f57dbee8a64ab823eb2bfe882d8ada259e1772c8dc95", + "identicalToBaseline": false + } + ] + }, + "historicalRunsWithTerminalCursorCount": 5, + "verification": { + "benchmarkTests": 99, + "passed": true, + "negativeCases": [ + "terminal none", + "source/prose next= text", + "multiple footers", + "repeated cursor" + ], + "positiveCases": [ + "valid opaque cursor", + "legacy page footer", + "discovery range footer", + "typed page/range footer", + "LF and CRLF" + ] + }, + "retainedFailedAttempts": [ + "First test invocation targeted the wrong unittest class; the second had an incorrect argument-position assertion. The corrected tests-before-03 invocation still failed on the real parser defects.", + "First replay collected 110 requests but used the Compass checkout cwd, losing eight source excerpts; it is invalidated, not a comparative result. Its summary step also required recovery of omitted graphMetrics. No production cache defect is inferred.", + "A byte-equality assertion stopped on three Graphify output variations; all are retained and reported separately from score agreement.", + "The successful replay-02 uses the final CRLF-capable runner, source cwd, all binary/graph/source pre/post checks and complete aggregation." + ], + "reduxDiagnosis": { + "scope": "Separate post-output Compass-only diagnostic, not comparative scoring.", + "originalQuestion": { + "seeds": 0, + "candidateNodesRead": 431, + "candidateProbes": 73, + "diagnostic": "bounded_truncation" + }, + "shortNameAndPhraseQueries": "kind of, kindOf, miniKindOf, and kindOf.kindOf produce source-located candidates. The natural-language retrieval/specificity gap remains open." + }, + "artifactRoot": "cursor-harness-correction", + "artifactsSha256": { + "delta.json": "3373fd204948ed362058fdeee550b681e6736e2c2fc8950e3001334f7869865b", + "historical-terminal-cursor-census.json": "ac6b764e5f2d96da0ee1d727ae470d805893a1f5be0ecf2fa9dddb645e2d63de", + "raw-capture-manifest.json": "40a01acc3d0ce4be0bb191bccff01753e58eda98b351bb690fd1ffa3f0b98a9f", + "redux-recall-diagnostic/review.json": "d9b7cfb1c39d06bd847fa88fbd96a77513c6cd6a8ef073795fd4bf0b10aea1dd", + "replay-02/run.json": "ea64bb218186a65a775ddaefca65772b63835f98a267a8dfe7f5de29019d3c23", + "replay-02/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "replay-02.log": "c3865fd8355295d7be0078d9712075ea111dd4e3939b2b7ae09fb7d5d2044907", + "replay-02.py": "84080133c52c93e6e06b00b7dc2a208e529223bb953f78b9477524f530285f75", + "replay.log": "35238deb04a60cf3a91ed075ac657faa382bd9fea528d5fd3c81eb4ed2833e6b", + "replay.py": "a09dc5cd4a579bd7951cec7966f8030f3637d79e68e31d8e30a567045e80859e", + "run-before-summary.json": "894143f2b5fd53101685f9fb7dbbb86c058db6cd4a266a89e7185c395c516984", + "run.json": "afd0dbe34e18a8a1318ab42b5eec1f1f7789326ae7059ed11eaf3185b4317a7b", + "runner-replayed.py": "f77a8a67d8599696b9e8bec64f3244cc0bd8e63908ac1ac6ca6f2a5d0435c016", + "tests-after-02.log": "fa50898beef95c419d8fdf42dc636cacd8c65c993112f4e8cefe648cbe948530", + "tests-after.log": "b71895754d3cfc052c763f5ae4259d3472f0106d543cb347db4139df98a8a213", + "tests-before-02.log": "1f8b3d1a4bcfed1631fcfff1fead6291115d0eafff7a7d310dd5d25ac8b15c98", + "tests-before-03.log": "d24b646a92dc635cde717d4550ecb32e816b0bf1b67d868ce8d6ae741a295340", + "tests-before.log": "92f6d0896422342b4d02403844c3d2ba18e0c0f1fbc78a5de12ea7882d855091", + "tests-after-03.log": "b06670816c08ab1d2fdf397d80071ef2c7dd29e06feecd93aaa7864062330ac9", + "help-before.log": "ad9efaa4a0107c256eb59648686b65d6f1f70326a18d8a00d2ae65fa6c123064", + "help-after-baseline.log": "4c4807e45d9aa355b504f9291d3f8cd3f16570e7bd7125be910336d506fa2139", + "help-after.txt": "7b796d44eea2b37d0407e340bfcd70e41a757a4a8e688e24a0cffdbd03a69fc2", + "help-clippy.log": "66ee6744b47d9baf2ceda592ab6af4511ed61baee1b4143796d681fb7dc5e332" + }, + "testSourceSha256": "988baee76e896d97af6c306711e21beee67a97a6b56305db4591c228a3a80aaa", + "helpCorrection": { + "scope": "CLI help presentation only; runtime limits, graph/query schemas and release version are unchanged.", + "reproduction": "Replacing the numeric prefix default/hard maximum: 1000 also rewrote the 10000 expansion default to 128. Exact closing delimiters keep these fields separate.", + "sourceSha256": { + "crates/compass-cli/src/help.rs": "47322c5b7b5dceb0f94b2604f87dfbdca8746e4824b10418bdd5d7b39f527e68", + "crates/compass-cli/tests/help_cli.rs": "ebeb152bbc69daae78a256c1018ddf43bdf5d9041515c4da6e2de330379caa7e" + }, + "native": { + "passed": 1107, + "failed": 0, + "ignored": 2, + "scope": "Workspace lib/bin, help_cli and compass_product; --locked." + }, + "clippy": "Passed: workspace lib/bin plus help_cli, --locked, -D warnings.", + "binarySha256": "5e80b2f5e0624df1897fe7005677125d40fa991f4dfdf78089094dace2876ab8", + "graphFixtureQualification": "The complete fixture pass qualifies export-binding production 9b63873a. Not rerun for this help-only correction; no extraction/traversal code changed." + } +} diff --git a/benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java b/benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java new file mode 100644 index 000000000..22377b193 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java @@ -0,0 +1,14 @@ +package audit; + +public final class VarargsDemo { + static String join(String... values) { return "text"; } + static String join(int... values) { return "numbers"; } + static String join(boolean flag) { return "flag"; } + static String prefixed(String prefix, String... values) { return prefix; } + static String arrays(String[] values, int[] counts) { return "arrays"; } + static String text() { return join("a", "b"); } + static String numbers() { return join(1, 2); } + static String flag() { return join(true); } + static String explicitArray() { return join(new String[] {"a", "b"}); } + static String mixed() { return prefixed("p", "a", "b"); } +} diff --git a/benchmarks/agent_query/java_varargs_diagnostic_review.json b/benchmarks/agent_query/java_varargs_diagnostic_review.json new file mode 100644 index 000000000..eb079de9a --- /dev/null +++ b/benchmarks/agent_query/java_varargs_diagnostic_review.json @@ -0,0 +1,162 @@ +{ + "schema": "compass.java-varargs-diagnostic/1", + "scope": "Post-jsoup-output Java varargs development reduction; expected signatures and overload targets derive from this compiler-valid source before inspecting the diagnostic graph. No comparative score.", + "productionBinarySha256": "04eab05bd5bbc94f3f0f86580bdd3de9950599bc9e548b8f8ba95791b8baa0c9", + "sourceSha256": "2581dc8bb5b39948aee5df49900307a321460bf380d56daccbc9b401e9c0cd63", + "policySha256": "d07f29d1636932ea8ed6c108519fb7deb0d7dc20f1b0c87d3a68394cb12562e8", + "graphSha256": "881ea26d715768179c6ae3648eb961dbef5fd574a3b0ef29afd76a729016e2ff", + "javacVersion": "javac 17.0.8.1", + "javapSha256": "d79f5f02a538d36bfbafdcc34e32cd26d8caf221aa7dbe13341f3ab092f17020", + "compilerCheck": "javac succeeds; javap descriptors/call instructions independently support all five expected targets. Classes were not executed.", + "declarations": [ + { + "id": "sha256:351c542ad2e232f2ea0d17b89768c59fe7f477ecb4494ad2c33d9df5696eed24", + "line": 8, + "expected": "arrays(String[],int[])", + "actual": "arrays(String[],int[])", + "matched": true + }, + { + "id": "sha256:42e6c1d161fe53865950740560f34b0dcf3594f05a79debcadff1c9b3f13ad52", + "line": 5, + "expected": "join(int...)", + "actual": "join()", + "matched": false + }, + { + "id": "sha256:7273808751d5af1d86f5092e42120e07b56175d8d307cb0d312c3b7b7cef3677", + "line": 7, + "expected": "prefixed(String,String...)", + "actual": "prefixed(String)", + "matched": false + }, + { + "id": "sha256:8123f7ec5f2331101c918c18bb9399669ffab56f3a843eeb9d8083b6890cb0df", + "line": 4, + "expected": "join(String...)", + "actual": "join()", + "matched": false + }, + { + "id": "sha256:d066b45429219b124cf01c557aa12350030de07430407cadaa3675b709f80f5b", + "line": 6, + "expected": "join(boolean)", + "actual": "join(boolean)", + "matched": true + } + ], + "calls": [ + { + "caller": "text", + "callerId": "sha256:29868b4674ae3182cf0cd3c42560a1ce91f690b36c865f90b9be795dd947a3d0", + "expectedTargetLine": 4, + "matched": false, + "actualEdges": [] + }, + { + "caller": "numbers", + "callerId": "sha256:f9f687138af8cd065ddeabbce57e96f6d6167c755068facc1e35d2d233e49bb5", + "expectedTargetLine": 5, + "matched": false, + "actualEdges": [] + }, + { + "caller": "flag", + "callerId": "sha256:b9acf75cf87d34702e50e4b3f4662a52f46a3bdf390d1a823a9f450d9c65d603", + "expectedTargetLine": 6, + "matched": true, + "actualEdges": [ + { + "id": "sha256:f383ed947b1c6c09363fc97746ff511578a8f5fa0f11077784dc174c5c3c71ae", + "target": "sha256:d066b45429219b124cf01c557aa12350030de07430407cadaa3675b709f80f5b", + "targetLine": 6, + "site": { + "file": "VarargsDemo.java", + "startByte": 518, + "endByte": 522, + "startLine": 11, + "startColumn": 34, + "endLine": 11, + "endColumn": 38 + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.resolve.java.universal", + "confidence": "exact", + "rule": "universal-call-exact-lexical-declaration", + "anchors": [ + { + "file": "VarargsDemo.java", + "startByte": 518, + "endByte": 522, + "startLine": 11, + "startColumn": 34, + "endLine": 11, + "endColumn": 38 + } + ] + } + ] + } + ] + }, + { + "caller": "explicitArray", + "callerId": "sha256:a603dc4fd8be2f3032f4da006d2a519b5dab3369a9a659418f4f572c68a8a4ab", + "expectedTargetLine": 4, + "matched": false, + "actualEdges": [] + }, + { + "caller": "mixed", + "callerId": "sha256:7e5a305a9c07d2bb49f922cd757f4c094667bb90bae191b53a14fd1efd507d74", + "expectedTargetLine": 7, + "matched": true, + "actualEdges": [ + { + "id": "sha256:879485c67e8b7c9f6b104dc531cf819a1938e46e47e1833172caa8f0bea8e383", + "target": "sha256:7273808751d5af1d86f5092e42120e07b56175d8d307cb0d312c3b7b7cef3677", + "targetLine": 7, + "site": { + "file": "VarargsDemo.java", + "startByte": 643, + "endByte": 651, + "startLine": 13, + "startColumn": 35, + "endLine": 13, + "endColumn": 43 + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.resolve.java.universal", + "confidence": "exact", + "rule": "universal-call-explicit-binding", + "anchors": [ + { + "file": "VarargsDemo.java", + "startByte": 643, + "endByte": 651, + "startLine": 13, + "startColumn": 35, + "endLine": 13, + "endColumn": 43 + } + ] + } + ] + } + ] + } + ], + "findings": [ + "Both varargs-only join signatures omit their parameter types; prefixed omits its trailing spread parameter. Boolean and ordinary array signatures are preserved.", + "The graph preserves only two of five source/compiler-supported calls: flag and mixed. String, integer and explicit-array overload calls are missing. No wrong target is emitted for those three in this reduction.", + "java_parameter_signature and collect_java_parameter_type_nodes only read child_by_field_name(type); spread_parameter can lack that field. This is a code-inspection hypothesis until AST/native regression confirms the exact grammar shape." + ], + "inspectionCorrection": "Initial inspection looked for a top-level signature field. Corrected inspection reads details.data.signature; no claim that all signatures are absent.", + "status": "Unfixed; no native regression or comparative gain yet.", + "artifactRoot": "java-varargs-diagnostic", + "sourceFixture": "benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java" +} diff --git a/benchmarks/agent_query/runner.py b/benchmarks/agent_query/runner.py index f94600e99..0d0947c63 100644 --- a/benchmarks/agent_query/runner.py +++ b/benchmarks/agent_query/runner.py @@ -41,7 +41,7 @@ _COMMIT = re.compile(r"^[0-9a-f]{40}$") _SHA256 = re.compile(r"sha256:[0-9a-f]{64}") -_CURSOR = re.compile(r"next=([^\s]+)") +_CURSOR = re.compile(r"^Pagination:[^\r\n]*[ \t]next=([^\s]+)[ \t]*\r?$", re.MULTILINE) _GRAPHIFY_NODE = re.compile(r"^NODE (.+?) \[src=(\S+) loc=L(\d+)", re.MULTILINE) _GRAPHIFY_CANDIDATE = re.compile(r"^\s+id: (\S+)", re.MULTILINE) _COMPASS_ENTITY = re.compile(r"^- (\S+) \[[a-z_]+\] \S+:\d", re.MULTILINE) @@ -547,6 +547,7 @@ def run_question( failures: tuple[str, ...] = ("not executed",) passed = False pages: list[str] = [] + seen_cursors: set[str] = set() for attempt in range(question.max_follow_ups + 1): argv = (str(binary), *_tool_argv(tool, question, graph, budget=budget, cursor=cursor)) stem = f"{question.identifier}.{tool}.{attempt}" @@ -588,10 +589,13 @@ def run_question( if result.output_limited or result.timed_out or result.exit_code != 0: break if tool == "compass": - match = _CURSOR.search(result.stdout) - if match is None: + # A source excerpt can contain `next=...`; only one actual footer + # can authorize continuation. `none` is the end marker, not a token. + matches = _CURSOR.findall(result.stdout) + if len(matches) != 1 or matches[0] == "none" or matches[0] in seen_cursors: break - cursor = match.group(1) + cursor = matches[0] + seen_cursors.add(cursor) else: if question.kind != "broad": # Graphify documents a continuation for `query` only: it re-runs diff --git a/benchmarks/agent_query/tests/test_runner.py b/benchmarks/agent_query/tests/test_runner.py index 6e25758b0..90e7d1cf5 100644 --- a/benchmarks/agent_query/tests/test_runner.py +++ b/benchmarks/agent_query/tests/test_runner.py @@ -329,6 +329,53 @@ def test_failed_or_timed_out_output_cannot_pass(self) -> None: self.assertFalse(observation.first_page_pass) self.assertTrue(observation.failures) + def test_compass_continuation_requires_one_real_footer_cursor(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + outputs = ( + "Pagination: range=1-1 of 1 next=none\n", + "Source excerpt: next=source_text\n", + "Bound: next= continues the response\n", + "Pagination: page=1 range=1-2 of 4 next=first\nPagination: page=2 range=3-4 of 4 next=second\n", + ) + for output in outputs: + with self.subTest(output=output): + result = CommandResult((), 0, False, 1, len(output), 0, output, "") + with patch("benchmarks.agent_query.runner.run_bounded", return_value=result) as run: + observation = run_question( + repository, question(kind="broad", compass=("query", "sample"), max_follow_ups=2), tool="compass", + binary=Path("tool"), graph=Path("graph.json"), cwd=ROOT, + raw_dir=ROOT, timeout_seconds=1, + ) + self.assertEqual(run.call_count, 1) + self.assertEqual(observation.follow_ups, 0) + self.assertFalse(observation.passed) + self.assertEqual(observation.exit_code, 0) + + def test_compass_follows_footer_token_and_stops_on_repeated_cursor(self) -> None: + repository = load_suite(ROOT / "suite.toml").repositories[0] + for footer in ( + "Pagination: range=1-1 of 2 next=opaque-token", + "Pagination: page=1 range=1-1 of 2 next=opaque-token", + "Pagination: page=1/2 items=1-1/2 next=opaque-token", + "Pagination: range=1-1 of 2 next=opaque-token\r", + ): + for final in ("sample.go\n", footer + "\n"): + with self.subTest(footer=footer, final=final): + first = "source next=unrelated\n" + footer + "\n" + replies = [CommandResult((), 0, False, 1, len(s), 0, s, "") + for s in (first, final)] + with patch("benchmarks.agent_query.runner.run_bounded", side_effect=replies) as run: + observation = run_question( + repository, question(kind="broad", compass=("query", "sample"), max_follow_ups=3), tool="compass", + binary=Path("tool"), graph=Path("graph.json"), cwd=ROOT, + raw_dir=ROOT, timeout_seconds=1, + ) + self.assertEqual(run.call_count, 2) + argv = run.call_args_list[1].args[0] + self.assertEqual(argv[argv.index("--cursor") + 1], "opaque-token") + self.assertEqual(observation.follow_ups, 1) + self.assertEqual(observation.passed, final == "sample.go\n") + def test_snapshot_pointer_cannot_fall_back_to_an_unpublished_graph(self) -> None: with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) diff --git a/crates/compass-cli/src/help.rs b/crates/compass-cli/src/help.rs index 5684cf711..8367de58a 100644 --- a/crates/compass-cli/src/help.rs +++ b/crates/compass-cli/src/help.rs @@ -1113,12 +1113,12 @@ fn render_page(page: &Page, style: HelpStyle) -> String { "--format Discovery output", ) .replace( - "default/hard maximum: 500", - "default: 64; hard maximum: 500", + "default/hard maximum: 500]", + "default: 64; hard maximum: 500]", ) .replace( - "default/hard maximum: 1000", - "default: 128; hard maximum: 1000", + "default/hard maximum: 1000]", + "default: 128; hard maximum: 1000]", ) } else if matches!( page.path, diff --git a/crates/compass-cli/tests/help_cli.rs b/crates/compass-cli/tests/help_cli.rs index 6fc124129..a586fc490 100644 --- a/crates/compass-cli/tests/help_cli.rs +++ b/crates/compass-cli/tests/help_cli.rs @@ -108,6 +108,38 @@ fn command_and_nested_help_explain_options_and_examples() { } } +#[test] +fn discovery_help_keeps_returned_edges_and_examined_relationship_limits_distinct() { + let limits = compass_model::query_contract::DiscoveryLimits::default(); + for arguments in [["query", "--help"], ["help", "query"]] { + let outcome = invoke(&arguments); + assert_eq!(outcome.code, 0, "{}", outcome.stderr); + let text = outcome + .stdout + .split_whitespace() + .collect::>() + .join(" "); + assert!( + text.contains(&format!( + "--max-nodes Returned node count [default: {}; hard maximum: 500]", + limits.max_nodes + )), + "{text}" + ); + assert!( + text.contains(&format!( + "--max-edges Returned edge count [default: {}; hard maximum: 1000]", + limits.max_edges + )), + "{text}" + ); + assert!(text.contains(&format!( + "--max-expanded-relationships Examined relationships [default/hard maximum: {}]", + limits.max_expanded_relationships + )), "{text}"); + } +} + #[test] fn every_public_nested_command_has_a_dedicated_page() { for (parent, children) in [ diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 647e5d3d8..e0cab91de 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1674,6 +1674,79 @@ expressions. No graph, request, or witness changed. This is selected development evidence on reused graphs, with concurrent fixture qualification and no speed claim. It establishes no directed-navigation lead. +#### Terminal-cursor harness correction and remaining broad-query gap + +The benchmark previously treated any `next=` token as a continuation, including +the published terminal marker `next=none`. Redux's broad question therefore +received an unnecessary second request and an invalid-cursor error. The harness +now accepts one pagination footer, stops at `none` or a repeated token, and +ignores source/prose occurrences. Regressions cover real cursors, multiple +footers, legacy/discovery/typed footer forms, and LF/CRLF. All **99 benchmark +tests pass**. Graphify's documented budget continuation is unchanged. + +A query-only replay runs all 110 unchanged requests with the same binaries, +graph digests, source pins, and source working directories. Scores remain +**Compass 49/55, Graphify 46/55**. The sole outcome change removes the invalid +Redux follow-up: the question still fails its `miniKindOf` recall requirement, +now with exit zero and zero follow-ups. Every retained Compass capture is +byte-identical to its baseline page. Three Graphify query outputs vary in edge +order/content within their output budgets while scores and byte counts remain +unchanged; no whole-answer equivalence or causal claim follows. + +The first replay used the Compass checkout as its working directory, making +eight source excerpts unavailable. That attempt is explicitly invalidated and +retained, along with its later summary-generation error. No cache defect or +token improvement is inferred from it. The successful second replay preserves +both tools' original working-directory setup. A failed byte-equality assertion +also remains documented; it exposed the three Graphify variations rather than +a scoring change. The +[correction review](../../benchmarks/agent_query/cursor_harness_correction_review.json) +records exact runner, executable, graph, source, and raw-capture provenance. + +A separate post-output diagnostic leaves the product gap open: the original +Redux question reads 431 candidates in 73 probes but admits zero seeds and +reports bounded truncation. Shorter `kind of`, `kindOf`, `miniKindOf`, and +qualified-name questions recover source-located candidates. These are diagnosis +inputs, not replacements for the original failed question or extra comparative +passes. The specificity filter and phrase recall need a native reduction and +broader positive/negative evaluation before changing their behavior. + +During this investigation, help output incorrectly displayed an examined-edge +default of 128 even though runtime JSON and `DiscoveryLimits` use 10,000. A +numeric-prefix replacement intended for the 1,000 returned-edge ceiling also +matched 10,000. The help correction requires the closing delimiter. A native +regression failed before the change and checks both help entry points against +the runtime defaults. Verification passes **1,107 native tests, zero failed, +two ignored** across workspace libraries/binaries and help/product integration +targets. Workspace and help-integration Clippy pass with warnings denied, as do +formatting, diff, and the product boundary. Runtime query limits and graph/query +schemas do not change. Full fixture qualification was not repeated for this +help-only correction; the recorded pass qualifies query/cache commit `9b63873a`. + +#### Java varargs reduction: signature and call recall failures + +A [compiler-valid reduction](../../benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java) +now isolates the varargs issue found during jsoup review. Expected declarations +and call targets were written before extracting this diagnostic. `javac +17.0.8.1` compiles it, and `javap` descriptors/instructions independently confirm +the target overloads; compiled classes were not executed. + +The frozen export-binding binary preserves only **two of five** reviewed +declaration signatures: the boolean overload and ordinary arrays. Both +varargs-only `join` declarations render `join()`, and the prefixed overload +omits its trailing varargs parameter. More seriously, it preserves only **two +of five** reviewed calls: the boolean call and the uniquely named prefixed call. +Calls with string arguments, integer arguments, and an explicit string array +are missing. This reduction emits no wrong target for those three calls. + +The [diagnostic review](../../benchmarks/agent_query/java_varargs_diagnostic_review.json) +retains exact identities, source and graph hashes, compiler evidence, and the +unfixed failures. Initial inspection looked for a top-level signature; corrected +inspection reads `details.data.signature`. Code inspection suggests the producer +assumes every spread parameter has a named `type` field; AST inspection and a +failed-before native regression are still required. This is a post-jsoup-output +development reduction, not a new comparative result or a completed repair. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 16050196adca2b870c08b88ea66a2f07f8e391bf Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:12:30 -0700 Subject: [PATCH 38/97] fix: preserve Java varargs and overload invocation phases --- CHANGELOG.md | 5 + COMPATIBILITY.md | 10 + MIGRATION.md | 6 + .../java_varargs/ExtendedVarargs.java | 42 + .../java_varargs_development_review.json | 1018 +++++++++++++++++ .../java_varargs_diagnostic_review.json | 5 +- crates/compass-files/src/cache.rs | 2 +- crates/compass-files/tests/contracts.rs | 6 + .../compass-languages/src/evidence/build.rs | 119 +- .../compass-languages/tests/java_varargs.rs | 161 +++ .../src/evidence/languages/java.rs | 156 ++- .../src/evidence/resolve/members.rs | 7 +- crates/compass-resolve/tests/java_varargs.rs | 73 ++ .../tests/java_varargs_phases.rs | 203 ++++ ...ode-graph-intelligence-audit-2026-09-26.md | 84 +- docs/reference/universal-semantic-evidence.md | 7 +- 16 files changed, 1827 insertions(+), 77 deletions(-) create mode 100644 benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java create mode 100644 benchmarks/agent_query/java_varargs_development_review.json create mode 100644 crates/compass-languages/tests/java_varargs.rs create mode 100644 crates/compass-resolve/tests/java_varargs.rs create mode 100644 crates/compass-resolve/tests/java_varargs_phases.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 18eae11e6..cb0a3e659 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Preserve Java varargs signatures, parameter array types, and explicit array + argument dimensions. Resolve supported overloads in strict, loose, then + variable-arity order, retaining ambiguity when evidence cannot select a + unique target. Rebuild graphs to refresh older AST caches. + - Correct natural discovery help to report the 10,000 examined-relationship default independently of the 128 returned-edge default. Runtime limits are unchanged. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index aff14f4cc..cd808c7b2 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -231,6 +231,16 @@ to 5, rebuilding older disposable facts across languages and invalidating old build seals. Producer capabilities, graph/evidence schemas, and published historical realizations are unchanged. +Java varargs signatures now retain their spread parameter, and canonical +parameter types represent it as an array. Explicit array arguments and trailing +parameter dimensions preserve their rank. Supported overloads are considered +in strict fixed-arity, loose fixed-arity, then variable-arity phases. Missing +argument/hierarchy evidence and incomparable overloads remain unresolved; +unequal variadic prefixes do not establish a most-specific target. Rebuilding +can change signature metadata and call targets. AST +cache semantics advance from 5 to 6; published history, producer capabilities, +and graph/evidence schemas remain unchanged. + ### Framework route hierarchy Framework route hierarchy now requires a recognized filesystem-convention fact diff --git a/MIGRATION.md b/MIGRATION.md index 88c8ed211..b7ef8c0f8 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,12 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Rebuild Java graphs to receive corrected varargs signatures, array argument +types, and overload targets. Spread parameters now retain their declared array +type; calls may gain targets or select a different, source-supported overload. +Normal builds discard AST cache versions older than 6. Existing historical +graphs and the graph schema remain unchanged. + Name-based queries can now resolve a coincident export binding to its proven declaration. Use the exact export node ID when you want the binding record. This query correction works on existing graphs. Older traversal caches rebuild diff --git a/benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java b/benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java new file mode 100644 index 000000000..1423142ba --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java @@ -0,0 +1,42 @@ +package audit; + +public class ExtendedVarargs { + static void strings(String... values) {} + static void integers(int... values) {} + static void shape(String value) {} + static void shape(String[] value) {} + static void shape(String[][] value) {} + static void choose(Object... values) {} + static void choose(String... values) {} + static void phase(Object value) {} + static void phase(String... values) {} + static void widen(long value) {} + static void widen(Integer value) {} + static void loose(Integer value) {} + static void loose(int... values) {} + static void primitive(long... values) {} + static void primitive(int... values) {} + static void prefixed(String prefix, int... values) {} + + static void empty() { strings(); } + static void mostSpecificEmpty() { choose(); } + static void mostSpecificMany() { choose("a", "b"); } + static void nullArray() { choose((String[]) null); } + static void fixedBeforeExpanded() { phase("a"); } + static void wideningBeforeBoxing() { widen(1); } + static void boxingBeforeExpanded() { loose(1); } + static void primitiveSpecific() { primitive(1, 2); } + static void prefixEmpty() { prefixed("a"); } + static void arrayArgument() { shape(new String[1]); } + static void matrixArgument() { shape(new String[1][]); } + static void matrixInitializer() { shape(new String[][] {{"a"}}); } + static void spreadForward(final String... values) { shape(values); } + static void trailingDimensions(String values[]) { shape(values); } + static void repeated() { strings("a"); strings("b"); } + static void unknown(String value) { choose(value.toString()); } + + void receiver(ExtendedVarargs this, String... values) { shape(values); } + static void modified(@Deprecated final String... values) { shape(values); } + static void arrays(String[]... values) { shape(values); } + static void generic(java.util.List... values) {} +} diff --git a/benchmarks/agent_query/java_varargs_development_review.json b/benchmarks/agent_query/java_varargs_development_review.json new file mode 100644 index 000000000..fa7317151 --- /dev/null +++ b/benchmarks/agent_query/java_varargs_development_review.json @@ -0,0 +1,1018 @@ +{ + "schema": "compass.java-varargs-development-review/1", + "scope": "Repeated five-language development comparison. Source-reviewed Java delta and compiler-checked reductions; not fresh held-out confirmation, representative precision, community quality, or general superiority.", + "runId": "java-varargs-panel-a-02", + "suiteDigest": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "runnerDigest": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "tools": [ + { + "binary": "java-varargs-resolution-02/compass", + "binarySha256": "9b958b7f35919ccc47042dc304ce58a6890af8a803b3214b148bae0595be083d", + "digestScope": "executable-file-only", + "name": "compass", + "version": "compass 0.3.30", + "binaryLocationScope": "external evaluation artifact root" + }, + { + "binary": "graphify", + "binarySha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only", + "name": "graphify", + "version": "graphify 0.9.67", + "binaryLocationScope": "installed launcher; absolute location retained in external run manifest" + } + ], + "frozenManifestSha256": "7db16b8bd349284966ea8fb2f225e981dc76c867365929dc322e0ea28220abf8", + "productionSourceSha256": { + "crates/compass-files/src/cache.rs": "d1ec810e456af924c04e07adc01c29d25d73712ea70922550253c655bdd3bc89", + "crates/compass-languages/src/evidence/build.rs": "9427d1685e42b388bc4665e0bcfce54249441296f578e7a3d6a8e5261f355631", + "crates/compass-resolve/src/evidence/languages/java.rs": "ae08fc3c51a7cc90b1b233397faf9e1a493d68c884853eb0eb0fee06e3d91104", + "crates/compass-resolve/src/evidence/resolve/members.rs": "e612a3112f099158faa48a36cb28baf2e459db32fe5d368f6833d6e4bc0a4ba9" + }, + "metrics": { + "compass": { + "text": { + "passed": 49, + "questions": 55 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + }, + "selectedSourceRelationships": { + "passed": 17, + "allOccurrencesPassed": 17, + "total": 21 + } + }, + "graphify": { + "text": { + "passed": 46, + "questions": 55 + }, + "sourcePaths": { + "passed": 8, + "total": 10 + }, + "selectedSourceRelationships": { + "passed": 20, + "allOccurrencesPassed": 17, + "total": 21 + } + } + }, + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf", + "compassGraphByteIdenticalToPrevious": true, + "graphifyGraphByteIdenticalToPrevious": true + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234", + "compassGraphByteIdenticalToPrevious": true, + "graphifyGraphByteIdenticalToPrevious": true + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69", + "compassGraphByteIdenticalToPrevious": false, + "graphifyGraphByteIdenticalToPrevious": true + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b", + "compassGraphByteIdenticalToPrevious": true, + "graphifyGraphByteIdenticalToPrevious": true + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassBuildExit": 0, + "graphifyBuildExit": 0, + "compassGraphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd", + "compassGraphByteIdenticalToPrevious": true, + "graphifyGraphByteIdenticalToPrevious": true + } + ], + "diagnostic": { + "fixturePaths": [ + "benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java", + "benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java" + ], + "sourceSha256": { + "ExtendedVarargs.java": "b8423c3c341d366b6f81b032d5021e4819fcb97cc887e612eaac29635e0804f4", + "VarargsDemo.java": "2581dc8bb5b39948aee5df49900307a321460bf380d56daccbc9b401e9c0cd63" + }, + "compiler": "javac 17.0.8.1; javap descriptors and invoke instructions; classes not executed", + "javapSha256": "069cd6a68617f2d4eaf1c9427861f5373cbbb6a01560c350d32a83176d661546", + "before": { + "expectedOccurrences": 25, + "correctOccurrences": 6, + "wrongOccurrences": 5, + "missingOccurrences": 14 + }, + "after": { + "expectedOccurrences": 25, + "correctOccurrences": 24, + "wrongOccurrences": 0, + "missingOccurrences": 1 + }, + "remainingMiss": "ExtendedVarargs.unknown calls choose with a nested method-result expression. The compiler knows the String result; Compass retains an untyped argument and ambiguity. This is a recall miss, not an absence success.", + "beforeReviewSha256": "dc9c36a4218aa5eafdf537b6ac1de3424dee545d29b23dc8cc0fe29535be4d81", + "afterReviewSha256": "41471ea0635de694a92d0548358e6ac24939f63c4d8f9717ade02f3473365b27", + "oracleScope": "25 explicit calls to methods declared in the two fixture classes. External String.toString and compiler-inserted boxing/default-constructor instructions are outside this selected internal-call oracle." + }, + "jsoupDelta": { + "beforeCounts": { + "nodes": 6116, + "edges": 21095 + }, + "afterCounts": { + "nodes": 6116, + "edges": 21110 + }, + "signatureCorrections": 43, + "addedByKind": { + "references": 6, + "calls": 9 + }, + "removedByKind": {}, + "reviewedAddedRelationships": 15, + "addedCallsProduction": 5, + "addedCallsTests": 4, + "reviewScope": "All 43 changed signatures and all 15 added relationships reviewed against pinned jsoup source. This is the entire observed delta, not representative whole-graph precision. Five added calls are production source and four test source; all six added references are production source. No removed relationships.", + "reviewedArtifactSha256": "dc795537b527707a9a1f9ff36de9234831000d9aaf6d6a6b4b22b7f5336a2d8b", + "signatures": [ + { + "file": "src/main/java/org/jsoup/parser/ParseError.java", + "line": 17, + "before": "(CharacterReader,String)", + "after": "(CharacterReader,String,Object...)", + "sourceSha256": "73a1dbe9caf2a439051b5dd77af383ae339d034d24ec673f9634d092d23aa6f0" + }, + { + "file": "src/main/java/org/jsoup/parser/ParseError.java", + "line": 29, + "before": "(int,String)", + "after": "(int,String,Object...)", + "sourceSha256": "73a1dbe9caf2a439051b5dd77af383ae339d034d24ec673f9634d092d23aa6f0" + }, + { + "file": "src/main/java/org/jsoup/select/CombiningEvaluator.java", + "line": 90, + "before": "()", + "after": "(Evaluator...)", + "sourceSha256": "4d7d2aa47151f1be31769709738db73afafe65458871b817510925327c4fe1c8" + }, + { + "file": "src/main/java/org/jsoup/select/CombiningEvaluator.java", + "line": 134, + "before": "()", + "after": "(Evaluator...)", + "sourceSha256": "4d7d2aa47151f1be31769709738db73afafe65458871b817510925327c4fe1c8" + }, + { + "file": "src/main/java/org/jsoup/select/Elements.java", + "line": 45, + "before": "()", + "after": "(Element...)", + "sourceSha256": "b6ed7b3905a52d85658ba6e6b1347de84513b9c6b51e8c8f5879ee29865f2873" + }, + { + "file": "src/main/java/org/jsoup/select/Nodes.java", + "line": 45, + "before": "()", + "after": "(T...)", + "sourceSha256": "d4c8c088df00bf04e5c9cd7e47bda7b644a52552bf11387870f882f07b0ac7f2" + }, + { + "file": "src/main/java/org/jsoup/select/Selector.java", + "line": 293, + "before": "(String)", + "after": "(String,Object...)", + "sourceSha256": "5452b5f12561dedcb8b7dccfdf861c4159e1e4292fef6a2d0cb4f1636f8a84b9" + }, + { + "file": "src/main/java/org/jsoup/select/Selector.java", + "line": 297, + "before": "(Throwable,String)", + "after": "(Throwable,String,Object...)", + "sourceSha256": "5452b5f12561dedcb8b7dccfdf861c4159e1e4292fef6a2d0cb4f1636f8a84b9" + }, + { + "file": "src/main/java/org/jsoup/Connection.java", + "line": 288, + "before": "data()", + "after": "data(String...)", + "sourceSha256": "031151f474574f94a8c9176049a4d8a9f97b14c334cde9ffd1f5b05127acc062" + }, + { + "file": "src/main/java/org/jsoup/examples/HtmlToPlainText.java", + "line": 33, + "before": "main()", + "after": "main(String...)", + "sourceSha256": "3f7e6718b8518240a8d538f60030edeab37648c0b7e70b6c620ee26a434bac7e" + }, + { + "file": "src/main/java/org/jsoup/examples/ListLinks.java", + "line": 50, + "before": "print(String)", + "after": "print(String,Object...)", + "sourceSha256": "a4e4260d8e9671de4fa49d9a0016bec7170631efed2dc5422e4a7583a60cd86e" + }, + { + "file": "src/main/java/org/jsoup/examples/Wikipedia.java", + "line": 27, + "before": "log(String)", + "after": "log(String,String...)", + "sourceSha256": "abf753f2d99f3b5bbd83e55d8af178ebc5a01749a544cb5a0ec1d4f8331a0aa1" + }, + { + "file": "src/main/java/org/jsoup/helper/HttpConnection.java", + "line": 254, + "before": "data()", + "after": "data(String...)", + "sourceSha256": "9f439dc7f7aa03d18a1fbff716cfb562f9e2667b249a60dfc329f5232b3c1145" + }, + { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "line": 69, + "before": "expectNotNull(T,String)", + "after": "expectNotNull(T,String,Object...)", + "sourceSha256": "256262c372b996305f30d89e668cb5110d6b36b13afa551c8bfa3a5ee50c85e6" + }, + { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "line": 205, + "before": "fail(String)", + "after": "fail(String,Object...)", + "sourceSha256": "256262c372b996305f30d89e668cb5110d6b36b13afa551c8bfa3a5ee50c85e6" + }, + { + "file": "src/test/java/org/jsoup/integration/Benchmark.java", + "line": 34, + "before": "print(String)", + "after": "print(String,Object...)", + "sourceSha256": "e928f9cfbd7e67be3bb4dcee16a4023aa9971edf20f970190a95608e904473ce" + }, + { + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "line": 264, + "before": "in(String)", + "after": "in(String,String...)", + "sourceSha256": "7e61ba7e8630f101f0a74d56fb91237c8588fceeb5c472166c60926dd6047881" + }, + { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "line": 901, + "before": "insertChildren(int)", + "after": "insertChildren(int,Node...)", + "sourceSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1595, + "before": "assertClassList(String)", + "after": "assertClassList(String,String...)", + "sourceSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc" + }, + { + "file": "src/test/java/org/jsoup/nodes/ElementTest.java", + "line": 1585, + "before": "assertClassNames(String)", + "after": "assertClassNames(String,String...)", + "sourceSha256": "f3d201c08a41ee429f5fe9fb72a12bbb6a620592d1276e66d876022fb5ef39bc" + }, + { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 603, + "before": "addChildren()", + "after": "addChildren(Node...)", + "sourceSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec" + }, + { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "line": 618, + "before": "addChildren(int)", + "after": "addChildren(int,Node...)", + "sourceSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec" + }, + { + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 414, + "before": "consumeToAny()", + "after": "consumeToAny(char...)", + "sourceSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c" + }, + { + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 471, + "before": "consumeToAnySorted()", + "after": "consumeToAnySorted(char...)", + "sourceSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c" + }, + { + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "line": 617, + "before": "matchesAny()", + "after": "matchesAny(char...)", + "sourceSha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c" + }, + { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 765, + "before": "clearStackToContext()", + "after": "clearStackToContext(String...)", + "sourceSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f" + }, + { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "line": 743, + "before": "popStackToClose()", + "after": "popStackToClose(String...)", + "sourceSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenQueue.java", + "line": 436, + "before": "consumeEscapedCssIdentifier()", + "after": "consumeEscapedCssIdentifier(char...)", + "sourceSha256": "7a23106abcfc95d71a6b774bdc10c02d33a98b25cbc429ade2d94db5d69e0fb1" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenQueue.java", + "line": 135, + "before": "consumeToAny()", + "after": "consumeToAny(String...)", + "sourceSha256": "7a23106abcfc95d71a6b774bdc10c02d33a98b25cbc429ade2d94db5d69e0fb1" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenQueue.java", + "line": 72, + "before": "matchesAny()", + "after": "matchesAny(char...)", + "sourceSha256": "7a23106abcfc95d71a6b774bdc10c02d33a98b25cbc429ade2d94db5d69e0fb1" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenQueue.java", + "line": 454, + "before": "matchesCssIdentifier()", + "after": "matchesCssIdentifier(char...)", + "sourceSha256": "7a23106abcfc95d71a6b774bdc10c02d33a98b25cbc429ade2d94db5d69e0fb1" + }, + { + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "line": 354, + "before": "characterReferenceError(String)", + "after": "characterReferenceError(String,Object...)", + "sourceSha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f" + }, + { + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "line": 364, + "before": "error(String)", + "after": "error(String,Object...)", + "sourceSha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f" + }, + { + "file": "src/main/java/org/jsoup/parser/TreeBuilder.java", + "line": 312, + "before": "error(String)", + "after": "error(String,Object...)", + "sourceSha256": "6f210b4b6e1f5e5d1f81655aa279103bf101e5b1f0ec17ee8fe7035894c10746" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 303, + "before": "addAttributes(String)", + "after": "addAttributes(String,String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 455, + "before": "addProtocols(String,String)", + "after": "addProtocols(String,String,String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 254, + "before": "addTags()", + "after": "addTags(String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 335, + "before": "removeAttributes(String)", + "after": "removeAttributes(String,String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 485, + "before": "removeProtocols(String,String)", + "after": "removeProtocols(String,String,String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "line": 272, + "before": "removeTags()", + "after": "removeTags(String...)", + "sourceSha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/test/java/org/jsoup/select/CssTest.java", + "line": 200, + "before": "check(Elements)", + "after": "check(Elements,String...)", + "sourceSha256": "89fa0627ec7586eb2489524544566da9e9dd6877a6d2490e14f2fa0791ca14f1" + }, + { + "file": "src/test/java/org/jsoup/select/SelectorTest.java", + "line": 34, + "before": "assertSelectedIds(Elements)", + "after": "assertSelectedIds(Elements,String...)", + "sourceSha256": "0560f38b89cb82e8f7e12386d468b43bd069874941be5fa85ad3227363e18d66" + }, + { + "file": "src/test/java/org/jsoup/select/SelectorTest.java", + "line": 42, + "before": "assertSelectedOwnText(Elements)", + "after": "assertSelectedOwnText(Elements,String...)", + "sourceSha256": "0560f38b89cb82e8f7e12386d468b43bd069874941be5fa85ad3227363e18d66" + } + ], + "relationships": [ + { + "kind": "references", + "source": "org.jsoup.select.CombiningEvaluator::And::", + "target": "org.jsoup.select.Evaluator", + "targetSignature": "public abstract class Evaluator", + "targetSource": { + "file": "src/main/java/org/jsoup/select/Evaluator.java", + "startByte": 1213, + "endByte": 29100, + "startLine": 31, + "startColumn": 0, + "endLine": 1045, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/select/CombiningEvaluator.java", + "startByte": 2572, + "endByte": 2581, + "startLine": 90, + "startColumn": 12, + "endLine": 90, + "endColumn": 21 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Evaluator", + "sourceFileSha256": "4d7d2aa47151f1be31769709738db73afafe65458871b817510925327c4fe1c8", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "references", + "source": "org.jsoup.select.CombiningEvaluator::Or::", + "target": "org.jsoup.select.Evaluator", + "targetSignature": "public abstract class Evaluator", + "targetSource": { + "file": "src/main/java/org/jsoup/select/Evaluator.java", + "startByte": 1213, + "endByte": 29100, + "startLine": 31, + "startColumn": 0, + "endLine": 1045, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/select/CombiningEvaluator.java", + "startByte": 3911, + "endByte": 3920, + "startLine": 134, + "startColumn": 11, + "endLine": 134, + "endColumn": 20 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Evaluator", + "sourceFileSha256": "4d7d2aa47151f1be31769709738db73afafe65458871b817510925327c4fe1c8", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "references", + "source": "org.jsoup.select.Elements::", + "target": "org.jsoup.nodes.Element", + "targetSignature": "public class Element extends Node implements Iterable", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "startByte": 1780, + "endByte": 84172, + "startLine": 50, + "startColumn": 0, + "endLine": 2211, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/select/Elements.java", + "startByte": 1367, + "endByte": 1374, + "startLine": 45, + "startColumn": 20, + "endLine": 45, + "endColumn": 27 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Element", + "sourceFileSha256": "b6ed7b3905a52d85658ba6e6b1347de84513b9c6b51e8c8f5879ee29865f2873", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "calls", + "source": "org.jsoup.helper.HttpConnectionTest::data", + "target": "org.jsoup.Connection::data", + "targetSignature": "data(String...)", + "targetSource": { + "file": "src/main/java/org/jsoup/Connection.java", + "startByte": 12820, + "endByte": 12855, + "startLine": 288, + "startColumn": 4, + "endLine": 288, + "endColumn": 39 + }, + "site": { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "startByte": 10827, + "endByte": 10831, + "startLine": 273, + "startColumn": 12, + "endLine": 273, + "endColumn": 16 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "data", + "sourceFileSha256": "62d806a0f78b538163554bd09e4cd9cdd58f5a5dbfcaf1addf600dd73a357355", + "rationale": "Connection-typed receiver; four String literals exclude InputStream overload and select data(String...)." + } + }, + { + "kind": "calls", + "source": "org.jsoup.helper.HttpConnectionTest::throwsOnOddData", + "target": "org.jsoup.Connection::data", + "targetSignature": "data(String...)", + "targetSource": { + "file": "src/main/java/org/jsoup/Connection.java", + "startByte": 12820, + "endByte": 12855, + "startLine": 288, + "startColumn": 4, + "endLine": 288, + "endColumn": 39 + }, + "site": { + "file": "src/test/java/org/jsoup/helper/HttpConnectionTest.java", + "startByte": 10664, + "endByte": 10668, + "startLine": 267, + "startColumn": 16, + "endLine": 267, + "endColumn": 20 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "data", + "sourceFileSha256": "62d806a0f78b538163554bd09e4cd9cdd58f5a5dbfcaf1addf600dd73a357355", + "rationale": "Connection-typed receiver; three String literals exclude InputStream overload and select data(String...)." + } + }, + { + "kind": "calls", + "source": "org.jsoup.helper.Validate::assertFail", + "target": "org.jsoup.helper.Validate::fail", + "targetSignature": "fail(String)", + "targetSource": { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "startByte": 6375, + "endByte": 6462, + "startLine": 184, + "startColumn": 4, + "endLine": 186, + "endColumn": 5 + }, + "site": { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "startByte": 6733, + "endByte": 6737, + "startLine": 195, + "startColumn": 8, + "endLine": 195, + "endColumn": 12 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "fail", + "sourceFileSha256": "256262c372b996305f30d89e668cb5110d6b36b13afa551c8bfa3a5ee50c85e6", + "rationale": "One String argument selects fail(String) in the fixed-arity phase before fail(String,Object...)." + } + }, + { + "kind": "calls", + "source": "org.jsoup.helper.ValidateTest::expectNotNull", + "target": "org.jsoup.helper.Validate::expectNotNull", + "targetSignature": "expectNotNull(T)", + "targetSource": { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "startByte": 1710, + "endByte": 1888, + "startLine": 53, + "startColumn": 4, + "endLine": 57, + "endColumn": 5 + }, + "site": { + "file": "src/test/java/org/jsoup/helper/ValidateTest.java", + "startByte": 1771, + "endByte": 1784, + "startLine": 63, + "startColumn": 31, + "endLine": 63, + "endColumn": 44 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "expectNotNull", + "sourceFileSha256": "34dc5c7d4be83ab69a3c8d40ab65c2b33cd83a82a9801bfe3f913ea12feaf7f2", + "rationale": "The other expectNotNull overload requires at least two arguments; the one-argument generic declaration is the unique arity-compatible source target." + } + }, + { + "kind": "calls", + "source": "org.jsoup.helper.ValidateTest::expectNotNull", + "target": "org.jsoup.helper.Validate::expectNotNull", + "targetSignature": "expectNotNull(T)", + "targetSource": { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "startByte": 1710, + "endByte": 1888, + "startLine": 53, + "startColumn": 4, + "endLine": 57, + "endColumn": 5 + }, + "site": { + "file": "src/test/java/org/jsoup/helper/ValidateTest.java", + "startByte": 1921, + "endByte": 1934, + "startLine": 66, + "startColumn": 98, + "endLine": 66, + "endColumn": 111 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "expectNotNull", + "sourceFileSha256": "34dc5c7d4be83ab69a3c8d40ab65c2b33cd83a82a9801bfe3f913ea12feaf7f2", + "rationale": "Null is the sole argument; the other expectNotNull overload requires at least two arguments." + } + }, + { + "kind": "references", + "source": "org.jsoup.nodes.Element::insertChildren", + "target": "org.jsoup.nodes.Node", + "targetSignature": "public abstract class Node implements Cloneable", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 743, + "endByte": 38461, + "startLine": 26, + "startColumn": 0, + "endLine": 1089, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "startByte": 35239, + "endByte": 35243, + "startLine": 901, + "startColumn": 45, + "endLine": 901, + "endColumn": 49 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Node", + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "calls", + "source": "org.jsoup.nodes.Element::insertChildren", + "target": "org.jsoup.nodes.Node::addChildren", + "targetSignature": "addChildren(int,Node...)", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 21739, + "endByte": 23462, + "startLine": 618, + "startColumn": 4, + "endLine": 658, + "endColumn": 5 + }, + "site": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "startByte": 35561, + "endByte": 35572, + "startLine": 907, + "startColumn": 8, + "endLine": 907, + "endColumn": 19 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "addChildren", + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "rationale": "Element extends Node; index is int and children is Node[] from the spread declaration, selecting addChildren(int,Node...)." + } + }, + { + "kind": "calls", + "source": "org.jsoup.nodes.Element::prependChild", + "target": "org.jsoup.nodes.Node::addChildren", + "targetSignature": "addChildren(int,Node...)", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 21739, + "endByte": 23462, + "startLine": 618, + "startColumn": 4, + "endLine": 658, + "endColumn": 5 + }, + "site": { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "startByte": 33396, + "endByte": 33407, + "startLine": 857, + "startColumn": 8, + "endLine": 857, + "endColumn": 19 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "addChildren", + "sourceFileSha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340", + "rationale": "Element extends Node; literal zero plus a Node parameter selects addChildren(int,Node...)." + } + }, + { + "kind": "references", + "source": "org.jsoup.nodes.Node::addChildren", + "target": "org.jsoup.nodes.Node", + "targetSignature": "public abstract class Node implements Cloneable", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 743, + "endByte": 38461, + "startLine": 26, + "startColumn": 0, + "endLine": 1089, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 21262, + "endByte": 21266, + "startLine": 603, + "startColumn": 31, + "endLine": 603, + "endColumn": 35 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Node", + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "references", + "source": "org.jsoup.nodes.Node::addChildren", + "target": "org.jsoup.nodes.Node", + "targetSignature": "public abstract class Node implements Cloneable", + "targetSource": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 743, + "endByte": 38461, + "startLine": 26, + "startColumn": 0, + "endLine": 1089, + "endColumn": 1 + }, + "site": { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "startByte": 21777, + "endByte": 21781, + "startLine": 618, + "startColumn": 42, + "endLine": 618, + "endColumn": 46 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "Node", + "sourceFileSha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec", + "rationale": "The spread parameter directly names the target type; same-package ownership or explicit import establishes its qualified identity. This is a type reference, not a call." + } + }, + { + "kind": "calls", + "source": "org.jsoup.parser.HtmlTreeBuilder::popStackToClose", + "target": "org.jsoup.parser.HtmlTreeBuilder::isHtmlEl", + "targetSignature": "isHtmlEl(Element,String[])", + "targetSource": { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "startByte": 12169, + "endByte": 12305, + "startLine": 296, + "startColumn": 4, + "endLine": 298, + "endColumn": 5 + }, + "site": { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "startByte": 30598, + "endByte": 30606, + "startLine": 746, + "startColumn": 16, + "endLine": 746, + "endColumn": 24 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "isHtmlEl", + "sourceFileSha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f", + "rationale": "el is declared Element; elNames is String[] from the spread declaration, selecting isHtmlEl(Element,String[])." + } + }, + { + "kind": "calls", + "source": "org.jsoup.parser.Tokeniser::emitTagPending", + "target": "org.jsoup.parser.Tokeniser::error", + "targetSignature": "error(String)", + "targetSource": { + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "startByte": 15235, + "endByte": 15362, + "startLine": 359, + "startColumn": 4, + "endLine": 362, + "endColumn": 5 + }, + "site": { + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "startByte": 12188, + "endByte": 12193, + "startLine": 268, + "startColumn": 16, + "endLine": 268, + "endColumn": 21 + }, + "review": { + "verdict": "source-supported", + "sourceSpan": "error", + "sourceFileSha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f", + "rationale": "A String literal selects error(String) before expanded error(String,Object...); TokeniserState is a distinct type." + } + } + ] + }, + "cacheUpgrade": { + "scope": "Final candidate: copied AST-v5 output upgrade and warm AST-v6 reuse compared with a clean build", + "graphBytesEqual": false, + "structureExcludingCommunityEqual": true, + "communityMemberPartitionsEqual": true, + "communityLabelsEqual": true, + "changedNodeFields": [ + "community.id" + ], + "cleanGraphSha256": "0e1536673b320e35783b469a957596ce9be04d1c51a40ffa3ec3958cf69a7969", + "upgradedGraphSha256": "548fbd9c1c2386936b1380fa21cdd175cca6f1fe81f2823fdfa3247779ae327a", + "upgradeSnapshots": { + "snapshot-1790487911654345000-94920-0": "548fbd9c1c2386936b1380fa21cdd175cca6f1fe81f2823fdfa3247779ae327a", + "snapshot-1790487912542778000-94917-0": "548fbd9c1c2386936b1380fa21cdd175cca6f1fe81f2823fdfa3247779ae327a" + }, + "firstBuild": "Compass indexed 2 files (2 extracted, 0 cached): 50 nodes, 72 edges, 9 communities with clustering.", + "secondBuild": "Compass indexed 2 files (0 extracted, 2 cached): 50 nodes, 72 edges, 9 communities with clustering.", + "inspectionCorrection": "Initial candidate full byte-equality assertion failed; the final candidate confirms only community numeric IDs differ under previous-state remapping. No whole-graph clean/upgrade byte-equivalence claim." + }, + "verification": { + "workspaceLibBins": { + "passed": 1092, + "failed": 0, + "ignored": 2 + }, + "contractsProductAndUniversalResolution": { + "passed": 250, + "failed": 0 + }, + "focusedFinal": { + "passed": 9, + "failed": 0, + "scope": "Final focused cases; the two original language cases were rerun with one added array-receiver case and counted once." + }, + "harnessTests": { + "passed": 99, + "failed": 0 + }, + "clippyWorkspaceAndFocusedTests": "passed -D warnings", + "formatDiffAndProductBoundary": "passed", + "fullFixtureQualification": "running against final production sources", + "logsSha256": { + "java-varargs-diagnostic/native-before.log": "12e803e1b59043f0b96f32c90c6fa43604fff9eef11ad8fa2df11a52d48cfeb7", + "java-varargs-diagnostic/resolve-before.log": "5a1ca619ecb0675cef3e74616e159da654fa8c444b2ddc76d2a7414b0f0c85bc", + "java-varargs-diagnostic/producer-after-01.log": "f0f1a670a4330f65e1e75fee705972b05ce828ef710682840f6a854166aa614d", + "java-varargs-diagnostic/producer-only-resolve.log": "9a3a45166e24b7ef264875fc99ae49e18b3a126baf56c4f5f5b109e9e3d968ee", + "java-varargs-diagnostic/baseline-01.log": "2c1587620fcb3d378bd0c94bc0bf71c5b14ff9f10429d80d82f62ea80aeec761", + "java-varargs-diagnostic/contracts-01.log": "d70f9ffff231014739c5eeb5ea4b2c5a5cb0e9f6d3dbc84712cb8fd44d548f30", + "java-varargs-diagnostic/clippy-01.log": "adf1459c5cac778d6f7fd28395e465468c63f9e438d028ccbb600013437135b5", + "java-varargs-diagnostic/build-01.log": "d63af56313ebe58aef7f31611fe4fed72d54222db42bd84943bd3211ab438c5f", + "java-varargs-resolution/native-final.log": "bf1516ce088be591d99de141dab2046aec23e2fff636656d303fe2948651be00", + "java-varargs-resolution/clippy-tests.log": "b47a9884711b2e035b98cd955bee4af1e70ba7dd46890a993d299dbadc42eb9b", + "java-varargs-diagnostic/harness-tests-02.log": "8fedd09e36ee392c613b4ccfd37584791bc391e9fc4c2ff42816a7cbdd348817", + "java-varargs-resolution-02/native-01.log": "44b07e649c453850e1d78dddd17057855bdf028b06b7335dc905cebdbd7d649e", + "java-varargs-resolution-02/baseline-01.log": "ee6dc64908c834664e2e68bf9b553cd755b1a8e6dbe048b5ac393fb31e9cba7f", + "java-varargs-resolution-02/contracts-01.log": "34bd55d1c3416441534f72ce92506738bf2f190d3bc192b4e7c836ed78105653", + "java-varargs-resolution-02/clippy-01.log": "6295551a2ff153dac1a505ad7f0aa8ed54356d6009dbd4aea999ee310e08aaee", + "java-varargs-resolution-02/clippy-tests.log": "fac2c1f1a82ac72753325b2f827dc4175737176ce2e24891453446a5c3e5aba5", + "java-varargs-resolution-02/build-01.log": "417587ddafc7f98dcabb9a1e2a27c82e820f932ffa72d1bc535b88b7ad1fc735", + "java-varargs-resolution-02/array-receiver-native.log": "5bfc3defd7d1840c09033831db91e5acd305ab0f400c31be7ee5d407a3da2a39", + "java-varargs-resolution-02/array-receiver-clippy.log": "44af7ac5bc4dcde7a22e573735545e22d984787c7ac702a061db4bd5f2142562" + }, + "additionalArrayReceiverRegression": { + "status": "passed; spread, ordinary-array, and trailing-dimension parameters retain unresolved clone occurrences without an invented element/array type receiver", + "sourceSha256": "fc0ad39c4b606aeefc507d2c06a5d44cbfb4510e100c41bb93b9c5536686a71c", + "nativeLogSha256": "5bfc3defd7d1840c09033831db91e5acd305ab0f400c31be7ee5d407a3da2a39", + "clippyLogSha256": "44af7ac5bc4dcde7a22e573735545e22d984787c7ac702a061db4bd5f2142562", + "productionSourcesChanged": false + }, + "missingParameterTypesRegression": { + "before": "failed: an absent optional vector incorrectly selected a zero-argument overload", + "after": "passed: the exact-match shortcut now requires a complete parameter vector", + "beforeLogSha256": "9e9951ef1a3909d7a5b32f2eee93ad45f2be081f395584fe675870033d04d1fe", + "afterLogSha256": "44b07e649c453850e1d78dddd17057855bdf028b06b7335dc905cebdbd7d649e" + }, + "supersededFirstQualification": { + "exitCode": 0, + "logSha256": "92cc06186a8584921cb7faddc60d8abd060c8a4be2bd731ad6c7d20b7dc9a4a7", + "releaseBinarySha256": "6d6c0a4caddf791ecd669367535f153929e8e95ef2f571cacdc5fdbeff20fe5f", + "scope": "Superseded run: started before the late zero-argument missing-types guard and ended after that edit. Retained as diagnostic history; not final-source qualification evidence." + } + }, + "limitations": [ + "Comparison scores are unchanged by this fix. The original reused questions remain unchanged, including failures.", + "Corrected Click occurrence witness remains a post-output diagnostic; original oracle is retained.", + "No fresh directed-path or MCP comparison, representative edge precision, responsibility/community quality, or god-object diagnosis established.", + "Unequal varargs prefixes, untyped arguments, incomplete hierarchy, and compiler-level generic inference remain conservative limitations.", + "Initial benchmark unittest discovery command ran zero tests; corrected tests directory ran 99 successfully.", + "Initial whole-graph cache equality assertion failed; structural records and community membership are equal but preserved community numeric IDs differ.", + "No latency claim; concurrent builds and executions share the machine." + ], + "artifactRoot": "java-varargs-resolution-02", + "specification": [ + "https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.4.1", + "https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.12.2" + ], + "intermediateRunId": "java-varargs-panel-a-01", + "finalGraphsByteIdenticalToIntermediate": true +} diff --git a/benchmarks/agent_query/java_varargs_diagnostic_review.json b/benchmarks/agent_query/java_varargs_diagnostic_review.json index eb079de9a..1aff964cc 100644 --- a/benchmarks/agent_query/java_varargs_diagnostic_review.json +++ b/benchmarks/agent_query/java_varargs_diagnostic_review.json @@ -156,7 +156,8 @@ "java_parameter_signature and collect_java_parameter_type_nodes only read child_by_field_name(type); spread_parameter can lack that field. This is a code-inspection hypothesis until AST/native regression confirms the exact grammar shape." ], "inspectionCorrection": "Initial inspection looked for a top-level signature field. Corrected inspection reads details.data.signature; no claim that all signatures are absent.", - "status": "Unfixed; no native regression or comparative gain yet.", + "status": "Historical before-fix diagnostic; preserved failures. See followupReview for the subsequent native correction and repeated development comparison.", "artifactRoot": "java-varargs-diagnostic", - "sourceFixture": "benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java" + "sourceFixture": "benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java", + "followupReview": "java_varargs_development_review.json" } diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index 8e46267c3..b5b3f5744 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "5"; +pub const AST_CACHE_VERSION: &str = "6"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index ded47a8fb..58e9be2ee 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -989,6 +989,11 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< // receiver. Those facts must not survive the semantics correction. fs::create_dir_all(cache_root.join("compass-out/cache/ast/v2/e1"))?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/v3/e1"))?; + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v5/e1"))?; + fs::write( + cache_root.join("compass-out/cache/ast/v5/e1/stale.msgpack"), + "stale Java spread parameter and array argument facts", + )?; fs::write( cache_root.join("compass-out/cache/ast/v3/e1/stale.msgpack"), "stale Go receiver facts", @@ -1026,6 +1031,7 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< assert!(!cache_root.join("compass-out/cache/ast/v0.9.21").exists()); assert!(!cache_root.join("compass-out/cache/ast/v2").exists()); assert!(!cache_root.join("compass-out/cache/ast/v3").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v5").exists()); let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index 5654c63fb..a625e45fb 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -3349,18 +3349,18 @@ impl<'source> DirectEvidenceState<'source> { if !matches!(parameter.kind(), "formal_parameter" | "spread_parameter") { continue; } - let Some(name) = parameter + let Some(name) = java_parameter_declarator(parameter) .child_by_field_name("name") .map(|node| self.text(node)) else { continue; }; - let Some(target) = parameter - .child_by_field_name("type") - .map(|node| java_normalize_type(&self.text(node))) - else { + let Some(mut target) = java_parameter_type_name(parameter, self.source) else { continue; }; + if parameter.kind() == "spread_parameter" { + target.push_str("[]"); + } self.java_value_types .entry(owner.scope_id.clone()) .or_default() @@ -3565,10 +3565,10 @@ impl<'source> DirectEvidenceState<'source> { } let type_root = match node.kind() { "method_declaration" => node.child_by_field_name("type"), - "field_declaration" - | "constant_declaration" - | "formal_parameter" - | "spread_parameter" => node.child_by_field_name("type"), + "field_declaration" | "constant_declaration" | "formal_parameter" => { + node.child_by_field_name("type") + } + "spread_parameter" => java_parameter_type_node(node), _ => None, }; if let Some(type_root) = type_root { @@ -3887,11 +3887,24 @@ impl<'source> DirectEvidenceState<'source> { } .to_owned(), ), - "object_creation_expression" | "array_creation_expression" => { + "object_creation_expression" => { expression.child_by_field_name("type").and_then(|target| { self.java_canonical_type(owner, &self.text(target), target.start_byte()) }) } + "array_creation_expression" => { + let target = expression.child_by_field_name("type")?; + let mut raw = self.text(target); + let mut cursor = expression.walk(); + for dimension in expression.named_children(&mut cursor) { + match dimension.kind() { + "dimensions_expr" => raw.push_str("[]"), + "dimensions" => raw.push_str(&java_dimensions_suffix(dimension)), + _ => {} + } + } + self.java_canonical_type(owner, &raw, target.start_byte()) + } "cast_expression" => expression.child_by_field_name("type").and_then(|target| { self.java_canonical_type(owner, &self.text(target), target.start_byte()) }), @@ -3958,6 +3971,10 @@ impl<'source> DirectEvidenceState<'source> { return owner.enclosing_type_qualified_name.clone(); } if let Some(target) = self.local_java_value_type(owner, receiver) { + // An array receiver is not an instance of its element class. + if target.ends_with("[]") { + return None; + } return self.java_qualified_type(owner, target, use_start); } if receiver @@ -9126,6 +9143,63 @@ fn last_java_import_name(node: Node<'_>) -> Option> { .last() } +fn java_parameter_type_node(parameter: Node<'_>) -> Option> { + parameter.child_by_field_name("type").or_else(|| { + if parameter.kind() != "spread_parameter" { + return None; + } + // The pinned Java grammar leaves spread types unnamed, unlike formal + // parameters. Inspect only direct type children, never annotations or + // the nested variable declarator. + let mut cursor = parameter.walk(); + parameter.named_children(&mut cursor).find(|child| { + matches!( + child.kind(), + "type_identifier" + | "scoped_type_identifier" + | "generic_type" + | "array_type" + | "annotated_type" + | "integral_type" + | "floating_point_type" + | "boolean_type" + ) + }) + }) +} + +fn java_parameter_declarator(parameter: Node<'_>) -> Node<'_> { + if parameter.kind() == "spread_parameter" { + let mut cursor = parameter.walk(); + if let Some(declarator) = parameter + .named_children(&mut cursor) + .find(|child| child.kind() == "variable_declarator") + { + return declarator; + } + } + parameter +} + +fn java_dimensions_suffix(dimensions: Node<'_>) -> String { + let mut cursor = dimensions.walk(); + dimensions + .children(&mut cursor) + .filter(|child| child.kind() == "[") + .map(|_| "[]") + .collect() +} + +fn java_parameter_type_name(parameter: Node<'_>, source: &[u8]) -> Option { + let type_node = java_parameter_type_node(parameter)?; + let mut normalized = java_normalize_type(type_node.utf8_text(source).ok()?); + if let Some(dimensions) = java_parameter_declarator(parameter).child_by_field_name("dimensions") + { + normalized.push_str(&java_dimensions_suffix(dimensions)); + } + Some(normalized) +} + fn java_parameter_signature(node: Node<'_>, source: &[u8]) -> (String, u32, bool, Vec) { let Some(parameters) = node.child_by_field_name("parameters") else { return (String::new(), 0, false, Vec::new()); @@ -9138,31 +9212,18 @@ fn java_parameter_signature(node: Node<'_>, source: &[u8]) -> (String, u32, bool .children(&mut cursor) .filter(|child| child.is_named()) { - if !matches!( - parameter.kind(), - "formal_parameter" | "spread_parameter" | "receiver_parameter" - ) { + if !matches!(parameter.kind(), "formal_parameter" | "spread_parameter") { continue; } variadic |= parameter.kind() == "spread_parameter"; - let Some(type_node) = parameter.child_by_field_name("type") else { + let Some(mut normalized) = java_parameter_type_name(parameter, source) else { continue; }; - let raw = type_node.utf8_text(source).unwrap_or_default(); - let mut normalized = java_normalize_type(raw); - canonical_inputs.push(normalized.clone()); if parameter.kind() == "spread_parameter" { + canonical_inputs.push(format!("{normalized}[]")); normalized.push_str("..."); - } - if let Some(dimensions) = parameter.child_by_field_name("dimensions") { - normalized.push_str( - &dimensions - .utf8_text(source) - .unwrap_or_default() - .chars() - .filter(|character| !character.is_whitespace()) - .collect::(), - ); + } else { + canonical_inputs.push(normalized.clone()); } types.push(normalized); } @@ -9286,7 +9347,7 @@ fn collect_java_parameter_type_nodes<'tree>( .filter(|child| child.is_named()) { if matches!(parameter.kind(), "formal_parameter" | "spread_parameter") - && let Some(type_node) = parameter.child_by_field_name("type") + && let Some(type_node) = java_parameter_type_node(parameter) { collect_java_type_nodes(type_node, output); } diff --git a/crates/compass-languages/tests/java_varargs.rs b/crates/compass-languages/tests/java_varargs.rs new file mode 100644 index 000000000..3926a3545 --- /dev/null +++ b/crates/compass-languages/tests/java_varargs.rs @@ -0,0 +1,161 @@ +use std::error::Error; +use std::path::Path; + +use compass_languages::{CandidateRelation, Engine, EvidenceLimits, validate_evidence}; + +const SOURCE: &str = + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java"); + +#[test] +fn java_array_parameter_receivers_remain_unresolved_instead_of_naming_element_types() +-> Result<(), Box> { + let source = br#"package audit; +class Item { public Item clone() { return this; } } +class Use { + void spread(Item... values) { values.clone(); } + void fixed(Item[] values) { values.clone(); } + void suffix(Item values[]) { values.clone(); } +} +"#; + let extraction = Engine::default().extract_source(Path::new("Use.java"), source)?; + let evidence = extraction.semantic_evidence.ok_or("missing evidence")?; + validate_evidence(&evidence, EvidenceLimits::default())?; + let calls = evidence + .candidates + .iter() + .filter(|c| c.relation == CandidateRelation::Calls && c.target_spelling == "clone") + .collect::>(); + assert_eq!(calls.len(), 3); + for call in calls { + assert!(call.constraints.qualified_name.is_none()); + assert!(!call.constraints.allow_external); + let occurrence = evidence + .occurrences + .iter() + .find(|o| Some(&o.id) == call.occurrence_id.as_ref()) + .ok_or("missing clone occurrence")?; + assert_eq!(occurrence.qualifier.as_deref(), Some("values")); + assert_eq!( + source.get( + usize::try_from(occurrence.range.start_byte)? + ..usize::try_from(occurrence.range.end_byte)? + ), + Some(b"clone".as_slice()) + ); + } + Ok(()) +} + +#[test] +fn java_spread_parameter_ast_and_signatures() -> Result<(), Box> { + let mut parser = tree_sitter::Parser::new(); + parser.set_language(&tree_sitter_language_pack::get_language("java")?)?; + let tree = parser + .parse(SOURCE, None) + .ok_or("missing Java syntax tree")?; + assert!(!tree.root_node().has_error()); + assert!( + tree.root_node().to_sexp().contains( + "(spread_parameter (type_identifier) (variable_declarator name: (identifier)))" + ) + ); + let extraction = + Engine::default().extract_source(Path::new("VarargsDemo.java"), SOURCE.as_bytes())?; + let evidence = extraction + .semantic_evidence + .ok_or("missing Java evidence")?; + validate_evidence(&evidence, EvidenceLimits::default())?; + for (line, signature, types, count, variadic) in [ + (4, "join(String...)", vec!["java.lang.String[]"], 1, true), + (5, "join(int...)", vec!["int[]"], 1, true), + (6, "join(boolean)", vec!["boolean"], 1, false), + ( + 7, + "prefixed(String,String...)", + vec!["java.lang.String", "java.lang.String[]"], + 2, + true, + ), + ( + 8, + "arrays(String[],int[])", + vec!["java.lang.String[]", "int[]"], + 2, + false, + ), + ] { + let declaration = evidence + .declarations + .iter() + .find(|d| d.kind == "method" && d.range.start_line == line) + .ok_or("missing method")?; + assert_eq!(declaration.signature.as_deref(), Some(signature)); + assert_eq!(declaration.parameter_types, types); + assert_eq!(declaration.parameter_count, Some(count)); + assert_eq!(declaration.variadic, variadic); + } + Ok(()) +} + +#[test] +fn java_varargs_preserves_dimensions_receivers_and_parameter_value_types() +-> Result<(), Box> { + let source = + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java"); + let extraction = + Engine::default().extract_source(Path::new("ExtendedVarargs.java"), source.as_bytes())?; + let evidence = extraction.semantic_evidence.ok_or("missing evidence")?; + validate_evidence(&evidence, EvidenceLimits::default())?; + for (name, signature, parameter_type) in [ + ( + "spreadForward", + "spreadForward(String...)", + "java.lang.String[]", + ), + ( + "trailingDimensions", + "trailingDimensions(String[])", + "java.lang.String[]", + ), + ("receiver", "receiver(String...)", "java.lang.String[]"), + ("modified", "modified(String...)", "java.lang.String[]"), + ("arrays", "arrays(String[]...)", "java.lang.String[][]"), + ("generic", "generic(java.util.List...)", "java.util.List[]"), + ] { + let declaration = evidence + .declarations + .iter() + .find(|d| d.name == name) + .ok_or("missing declaration")?; + assert_eq!(declaration.signature.as_deref(), Some(signature)); + assert_eq!(declaration.parameter_types, [parameter_type]); + assert_eq!(declaration.parameter_count, Some(1)); + } + for (name, argument_type) in [ + ("arrayArgument", "java.lang.String[]"), + ("matrixArgument", "java.lang.String[][]"), + ("matrixInitializer", "java.lang.String[][]"), + ("spreadForward", "java.lang.String[]"), + ("trailingDimensions", "java.lang.String[]"), + ("receiver", "java.lang.String[]"), + ("modified", "java.lang.String[]"), + ("arrays", "java.lang.String[][]"), + ] { + let declaration = evidence + .declarations + .iter() + .find(|d| d.name == name) + .ok_or("missing caller")?; + let call = evidence + .candidates + .iter() + .find(|c| c.source_declaration_id == declaration.id && c.target_spelling == "shape") + .ok_or("missing shape call")?; + assert_eq!( + call.constraints.argument_types, + [Some(argument_type.to_owned())], + "{name}" + ); + } + Ok(()) +} diff --git a/crates/compass-resolve/src/evidence/languages/java.rs b/crates/compass-resolve/src/evidence/languages/java.rs index eb33df41c..2502c8a8f 100644 --- a/crates/compass-resolve/src/evidence/languages/java.rs +++ b/crates/compass-resolve/src/evidence/languages/java.rs @@ -36,56 +36,120 @@ impl ResolutionDb<'_> { overloads: &[&'a DeclarationFact], argument_types: &[Option], ) -> Option<&'a str> { - let mut proven = Vec::new(); - for declaration in overloads { - if declaration.parameter_types.len() != argument_types.len() { + // JLS 15.12.2: strict fixed arity, loose fixed arity, then variable + // arity. A spread declaration participates in fixed phases as an array. + for phase in [ + JavaInvocationPhase::Strict, + JavaInvocationPhase::Loose, + JavaInvocationPhase::Variable, + ] { + let mut proven = Vec::new(); + let mut unknown = false; + for declaration in overloads { + match self.java_applicability(declaration, argument_types, phase) { + JavaApplicability::Proven => proven.push(*declaration), + JavaApplicability::Unknown => unknown = true, + JavaApplicability::Disproven => {} + } + } + // Missing type/hierarchy evidence in an earlier phase can change + // the selected overload. Never skip it to prefer a later phase. + if unknown { return None; } - let mut applicability = JavaApplicability::Proven; - for (parameter, argument) in declaration.parameter_types.iter().zip(argument_types) { - let argument = argument.as_deref()?; - match self.java_conversion(argument, parameter) { - JavaConversion::Proven => {} - JavaConversion::Disproven => { - applicability = JavaApplicability::Disproven; - break; - } - JavaConversion::Unknown => applicability = JavaApplicability::Unknown, - } + if proven.is_empty() { + continue; } - match applicability { - JavaApplicability::Proven => proven.push(*declaration), - JavaApplicability::Unknown => return None, - JavaApplicability::Disproven => {} + if let [only] = proven.as_slice() { + return Some(only.id.as_str()); } + let mut most_specific = proven.iter().copied().filter(|candidate| { + proven.iter().copied().all(|other| { + candidate.id == other.id + || self.java_parameters_more_specific(candidate, other, phase) + }) + }); + let only = most_specific.next()?; + return most_specific.next().is_none().then_some(only.id.as_str()); } - if let [only] = proven.as_slice() { - return Some(only.id.as_str()); + None + } + + fn java_applicability( + &self, + declaration: &DeclarationFact, + arguments: &[Option], + phase: JavaInvocationPhase, + ) -> JavaApplicability { + let parameters = &declaration.parameter_types; + if declaration.parameter_count != u32::try_from(parameters.len()).ok() { + return JavaApplicability::Unknown; } - let mut most_specific = proven.iter().copied().filter(|candidate| { - proven.iter().copied().all(|other| { - candidate.id == other.id - || self.java_parameter_vector_more_specific(candidate, other) - }) - }); - let only = most_specific.next()?; - most_specific.next().is_none().then_some(only.id.as_str()) + if phase == JavaInvocationPhase::Variable { + if !declaration.variadic + || parameters.is_empty() + || arguments.len() < parameters.len() - 1 + { + return JavaApplicability::Disproven; + } + if parameters.last().is_none_or(|p| !p.ends_with("[]")) { + return JavaApplicability::Unknown; + } + } else if parameters.len() != arguments.len() { + return JavaApplicability::Disproven; + } + let mut applicability = JavaApplicability::Proven; + for (index, argument) in arguments.iter().enumerate() { + let Some(parameter) = java_invocation_parameter(declaration, index, phase) else { + return JavaApplicability::Unknown; + }; + let Some(argument) = argument.as_deref() else { + applicability = JavaApplicability::Unknown; + continue; + }; + match self.java_phase_conversion(argument, parameter, phase) { + JavaConversion::Proven => {} + JavaConversion::Disproven => return JavaApplicability::Disproven, + JavaConversion::Unknown => applicability = JavaApplicability::Unknown, + } + } + applicability } - pub(in crate::evidence) fn java_parameter_vector_more_specific( + fn java_parameters_more_specific( &self, candidate: &DeclarationFact, other: &DeclarationFact, + phase: JavaInvocationPhase, ) -> bool { + // Comparing unequal fixed prefixes needs additional JLS specificity + // evidence. Retain ambiguity instead of selecting by declaration order. candidate.parameter_types.len() == other.parameter_types.len() - && candidate - .parameter_types - .iter() - .zip(&other.parameter_types) - .all(|(candidate, other)| { - self.java_conversion(candidate, other) == JavaConversion::Proven - }) && candidate.parameter_types != other.parameter_types + && (0..candidate.parameter_types.len()).all(|index| { + let Some(candidate) = java_invocation_parameter(candidate, index, phase) else { + return false; + }; + let Some(other) = java_invocation_parameter(other, index, phase) else { + return false; + }; + self.java_phase_conversion(candidate, other, JavaInvocationPhase::Strict) + == JavaConversion::Proven + }) + } + + fn java_phase_conversion( + &self, + argument: &str, + parameter: &str, + phase: JavaInvocationPhase, + ) -> JavaConversion { + if phase == JavaInvocationPhase::Strict + && java_primitive_type(argument) != java_primitive_type(parameter) + { + return JavaConversion::Disproven; + } + self.java_conversion(argument, parameter) } fn java_conversion(&self, argument: &str, parameter: &str) -> JavaConversion { @@ -243,6 +307,26 @@ impl ResolutionDb<'_> { } } +#[derive(Clone, Copy, Eq, PartialEq)] +enum JavaInvocationPhase { + Strict, + Loose, + Variable, +} + +fn java_invocation_parameter( + declaration: &DeclarationFact, + index: usize, + phase: JavaInvocationPhase, +) -> Option<&str> { + let parameters = &declaration.parameter_types; + if phase == JavaInvocationPhase::Variable && index >= parameters.len().checked_sub(1)? { + parameters.last()?.strip_suffix("[]") + } else { + parameters.get(index).map(String::as_str) + } +} + #[derive(Clone, Copy, Eq, PartialEq)] enum JavaApplicability { Proven, diff --git a/crates/compass-resolve/src/evidence/resolve/members.rs b/crates/compass-resolve/src/evidence/resolve/members.rs index c4c69f9bd..f7feefca4 100644 --- a/crates/compass-resolve/src/evidence/resolve/members.rs +++ b/crates/compass-resolve/src/evidence/resolve/members.rs @@ -780,7 +780,7 @@ impl ResolutionDb<'_> { } let argument_types = &candidate.constraints.argument_types; if target.language != "java" - || argument_types.is_empty() + || candidate.constraints.argument_count != u32::try_from(argument_types.len()).ok() || argument_types.iter().any(Option::is_none) { return true; @@ -802,7 +802,10 @@ impl ResolutionDb<'_> { .iter() .copied() .filter(|declaration| { - declaration.parameter_types.len() == argument_types.len() + // An absent optional type vector does not prove an empty + // parameter list, especially for zero-argument varargs. + declaration.parameter_count == u32::try_from(declaration.parameter_types.len()).ok() + && declaration.parameter_types.len() == argument_types.len() && declaration .parameter_types .iter() diff --git a/crates/compass-resolve/tests/java_varargs.rs b/crates/compass-resolve/tests/java_varargs.rs new file mode 100644 index 000000000..dd1574b8b --- /dev/null +++ b/crates/compass-resolve/tests/java_varargs.rs @@ -0,0 +1,73 @@ +use std::collections::{BTreeMap, HashMap}; +use std::error::Error; +use std::path::Path; + +use compass_languages::{Engine, EvidenceLimits, validate_evidence}; +use compass_resolve::resolve; + +#[test] +fn java_varargs_resolves_compiler_supported_overloads() -> Result<(), Box> { + let source = + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java"); + let extraction = + Engine::default().extract_source(Path::new("VarargsDemo.java"), source.as_bytes())?; + validate_evidence( + extraction + .semantic_evidence + .as_ref() + .ok_or("missing evidence")?, + EvidenceLimits::default(), + )?; + let sources = HashMap::from([("VarargsDemo.java".to_owned(), source.to_owned())]); + let resolved = resolve(&[extraction], &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let mut calls = BTreeMap::>::new(); + for edge in &resolved.edges { + if edge.string("relation") != "calls" || !edge.string("rule").starts_with("universal-call-") + { + continue; + } + let caller = resolved + .nodes + .iter() + .find(|node| node.id == edge.source) + .ok_or("missing caller")?; + let target = resolved + .nodes + .iter() + .find(|node| node.id == edge.target) + .ok_or("missing target")?; + assert_eq!(edge.string("_origin"), "ast"); + assert_eq!(edge.string("confidence"), "EXTRACTED"); + calls + .entry(caller.string("qualified_name")) + .or_default() + .push(target.string("signature")); + } + assert_eq!( + calls, + BTreeMap::from([ + ( + "audit.VarargsDemo::text".to_owned(), + vec!["join(String...)".to_owned()] + ), + ( + "audit.VarargsDemo::numbers".to_owned(), + vec!["join(int...)".to_owned()] + ), + ( + "audit.VarargsDemo::flag".to_owned(), + vec!["join(boolean)".to_owned()] + ), + ( + "audit.VarargsDemo::explicitArray".to_owned(), + vec!["join(String...)".to_owned()] + ), + ( + "audit.VarargsDemo::mixed".to_owned(), + vec!["prefixed(String,String...)".to_owned()] + ), + ]) + ); + Ok(()) +} diff --git a/crates/compass-resolve/tests/java_varargs_phases.rs b/crates/compass-resolve/tests/java_varargs_phases.rs new file mode 100644 index 000000000..2c66d452c --- /dev/null +++ b/crates/compass-resolve/tests/java_varargs_phases.rs @@ -0,0 +1,203 @@ +use std::collections::{BTreeMap, HashMap}; +use std::error::Error; +use std::path::Path; + +use compass_languages::{CandidateRelation, Engine, EvidenceLimits, validate_evidence}; +use compass_resolve::resolve; + +#[test] +fn java_zero_argument_varargs_does_not_treat_missing_parameter_types_as_empty_parameters() +-> Result<(), Box> { + let source = + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java"); + let mut extraction = + Engine::default().extract_source(Path::new("ExtendedVarargs.java"), source.as_bytes())?; + let evidence = extraction + .semantic_evidence + .as_mut() + .ok_or("missing evidence")?; + let unknown = evidence + .declarations + .iter_mut() + .find(|d| d.signature.as_deref() == Some("choose(Object...)")) + .ok_or("missing overload")?; + unknown.parameter_types.clear(); + // Canonical parameter types are optional evidence. An absent vector does + // not turn this one-parameter declaration into a zero-parameter overload. + validate_evidence(evidence, EvidenceLimits::default())?; + let sources = HashMap::from([("ExtendedVarargs.java".to_owned(), source.to_owned())]); + let resolved = resolve(&[extraction], &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let caller = resolved + .nodes + .iter() + .find(|n| n.string("qualified_name") == "audit.ExtendedVarargs::mostSpecificEmpty") + .ok_or("missing caller")?; + assert!( + !resolved + .edges + .iter() + .any(|e| e.source == caller.id && e.string("rule").starts_with("universal-call-")), + "missing canonical types must retain ambiguity for zero-argument varargs" + ); + Ok(()) +} + +#[test] +fn java_varargs_keeps_invocation_phases_occurrences_and_unknown_targets() +-> Result<(), Box> { + let source = + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/ExtendedVarargs.java"); + let inputs = [ + ("ExtendedVarargs.java", source), + ( + "VarargsDemo.java", + include_str!("../../../benchmarks/agent_query/fixtures/java_varargs/VarargsDemo.java"), + ), + ]; + let sources = inputs + .into_iter() + .map(|(p, s)| (p.to_owned(), s.to_owned())) + .collect::>(); + let mut engine = Engine::default(); + let mut extractions = inputs + .into_iter() + .map(|(p, s)| engine.extract_source(Path::new(p), s.as_bytes())) + .collect::, _>>()?; + let evidence = extractions[0] + .semantic_evidence + .as_ref() + .ok_or("missing evidence")?; + validate_evidence(evidence, EvidenceLimits::default())?; + let unknown = evidence + .declarations + .iter() + .find(|d| d.name == "unknown") + .ok_or("missing unknown caller")?; + let unknown_call = evidence + .candidates + .iter() + .filter(|c| { + c.source_declaration_id == unknown.id + && c.relation == CandidateRelation::Calls + && c.target_spelling == "choose" + }) + .collect::>(); + assert_eq!( + unknown_call.len(), + 1, + "retain the unresolved source occurrence" + ); + assert_eq!(unknown_call[0].constraints.argument_types, [None]); + let repeated = evidence + .declarations + .iter() + .find(|d| d.name == "repeated") + .ok_or("missing repeated caller")?; + let repeated_calls = evidence + .candidates + .iter() + .filter(|c| { + c.source_declaration_id == repeated.id + && c.relation == CandidateRelation::Calls + && c.target_spelling == "strings" + }) + .collect::>(); + assert_eq!(repeated_calls.len(), 2); + assert_ne!( + repeated_calls[0].occurrence_id, + repeated_calls[1].occurrence_id + ); + for candidate in &evidence.candidates { + if candidate.relation != CandidateRelation::Calls { + continue; + } + let occurrence = evidence + .occurrences + .iter() + .find(|o| Some(&o.id) == candidate.occurrence_id.as_ref()) + .ok_or("missing occurrence")?; + assert_eq!( + source.get( + usize::try_from(occurrence.range.start_byte)? + ..usize::try_from(occurrence.range.end_byte)? + ), + Some(candidate.target_spelling.as_str()) + ); + } + let resolved = resolve(&extractions, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let mut calls = BTreeMap::>::new(); + for edge in &resolved.edges { + if edge.string("relation") != "calls" || !edge.string("rule").starts_with("universal-call-") + { + continue; + } + let caller = resolved + .nodes + .iter() + .find(|n| n.id == edge.source) + .ok_or("missing caller")?; + let target = resolved + .nodes + .iter() + .find(|n| n.id == edge.target) + .ok_or("missing target")?; + let name = caller.string("qualified_name"); + if let Some(name) = name.strip_prefix("audit.ExtendedVarargs::") + && target + .string("qualified_name") + .starts_with("audit.ExtendedVarargs::") + { + assert_eq!(edge.string("_origin"), "ast"); + assert_eq!(edge.string("confidence"), "EXTRACTED"); + calls + .entry(name.to_owned()) + .or_default() + .push(target.string("signature")); + } + } + let expected = [ + ("empty", "strings(String...)"), + ("mostSpecificEmpty", "choose(String...)"), + ("mostSpecificMany", "choose(String...)"), + ("nullArray", "choose(String...)"), + ("fixedBeforeExpanded", "phase(Object)"), + ("wideningBeforeBoxing", "widen(long)"), + ("boxingBeforeExpanded", "loose(Integer)"), + ("primitiveSpecific", "primitive(int...)"), + ("prefixEmpty", "prefixed(String,int...)"), + ("arrayArgument", "shape(String[])"), + ("matrixArgument", "shape(String[][])"), + ("matrixInitializer", "shape(String[][])"), + ("spreadForward", "shape(String[])"), + ("trailingDimensions", "shape(String[])"), + ("receiver", "shape(String[])"), + ("modified", "shape(String[])"), + ("arrays", "shape(String[][])"), + ] + .into_iter() + .map(|(name, signature)| (name.to_owned(), vec![signature.to_owned()])) + .chain([( + "repeated".to_owned(), + vec!["strings(String...)".to_owned(); 2], + )]) + .collect::>(); + // unknown's nested method-result argument has no proven type: retaining + // ambiguity is correct even though javac knows the String return type. + assert_eq!(calls, expected); + extractions.reverse(); + let reversed = resolve(&extractions, &sources); + assert!(reversed.error.is_none(), "{:?}", reversed.error); + let canonical = |graph: &compass_languages::Extraction| { + let mut edges = graph + .edges + .iter() + .map(serde_json::to_string) + .collect::, _>>()?; + edges.sort(); + Ok::<_, serde_json::Error>(edges) + }; + assert_eq!(canonical(&resolved)?, canonical(&reversed)?); + Ok(()) +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index e0cab91de..f9e92c826 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1742,10 +1742,82 @@ are missing. This reduction emits no wrong target for those three calls. The [diagnostic review](../../benchmarks/agent_query/java_varargs_diagnostic_review.json) retains exact identities, source and graph hashes, compiler evidence, and the unfixed failures. Initial inspection looked for a top-level signature; corrected -inspection reads `details.data.signature`. Code inspection suggests the producer -assumes every spread parameter has a named `type` field; AST inspection and a -failed-before native regression are still required. This is a post-jsoup-output -development reduction, not a new comparative result or a completed repair. +inspection reads `details.data.signature`. At that checkpoint, code inspection +suggested the producer assumed every spread parameter had a named `type` field; +AST inspection and a failed-before native regression were still required. This +was a post-jsoup-output development reduction. The correction below preserves +that original failure record. + +## Java varargs correction and repeated development comparison + +The native failed-before regressions confirmed the earlier diagnostic: the +pinned Java grammar represents a spread type as an unnamed child and its name +inside a variable declarator. The producer now preserves that type, its array +rank, the spread signature, and parameter references. Explicit array creation +arguments retain their dimensions. The resolver checks strict fixed-arity, +loose fixed-arity, then variable-arity applicability and requires sufficient +evidence for a unique overload. Receiver parameters do not add call arguments; +array parameters do not borrow their element class as a method receiver. +AST cache semantics advance from 5 to 6; the product remains 0.3.30. + +The original five-call reduction now resolves all five calls. A larger, +compiler-checked reduction exposes both missing and wrong targets: + +| Selected source occurrences | Before | After | +| --- | ---: | ---: | +| Correct target | 6/25 | 24/25 | +| Wrong target | 5/25 | 0/25 | +| Missing | 14/25 | 1/25 | + +Four wrong targets treated array arguments as scalar strings; another selected +boxing before primitive widening. `javac`/`javap` confirm the expected targets; +the classes were not executed. The remaining missing call passes a method +result with no proven type in Compass. Its preserved ambiguity is a known +recall gap, not a negative success. These deliberately constructed development +cases do not estimate population precision. + +The fresh paired five-language run `java-varargs-panel-a-02` completed all 110 +requests with the original questions and witnesses. Text checks remain +**49/55 Compass, 46/55 Graphify**; reviewed source paths remain **8/10 each**. +Selected relationship identity checks remain **17/21 versus 20/21**; +all-occurrence checks remain **17/21 each** with the disclosed corrected Click +witness. This repair produced **no score gain on those existing questions**. +All four non-Java Compass graphs and all five Graphify graphs are byte-identical +to the export-binding checkpoint. + +The pinned jsoup graph keeps 6,116 nodes and grows from 21,095 to 21,110 edges: +43 signature corrections, nine added calls (five production, four test), and +six added production type references, with no removed relationships. Every +changed signature and added relationship was reviewed against the pinned +source, including overload sets, receiver declarations, inheritance, and +occurrence spans. This reviews the entire observed delta, not the remaining +graph or community responsibilities. + +An upgrade from copied AST-v5 artifacts extracts both files again; a subsequent +run reuses both AST-v6 entries. Links, node fields excluding community labels, +and community member partitions match a clean build. The initial byte-equality +assertion failed because community numeric IDs are remapped against prior +state. The two upgraded graphs are byte-identical to each other; whole-graph +equality with the clean build is not claimed. + +Verification so far: 1,092 workspace library/binary tests passed with two +ignored; 250 product/cache/resolution contract tests and nine focused tests +passed; workspace and focused-test Clippy passed with `-D warnings`; format, +diff, product-boundary, and all 99 benchmark harness tests passed. The full +fixture qualification is running against final production sources. Review found +that a missing optional type vector could incorrectly prove a zero-parameter +declaration; that regression failed before an added exact-match guard and passed +afterward. The earlier fixture run passed but is superseded by that source +change. The second frozen comparison reproduces all ten first-run graph hashes +and the same scores. An additional array-receiver regression passed for spread, +ordinary-array, and trailing-dimension parameters without changing production +code. The first +harness discovery command pointed at the wrong directory and ran zero tests; +the corrected command ran all 99. The +[development review](../../benchmarks/agent_query/java_varargs_development_review.json) +records binaries, sources, graph hashes, all source judgments, cache differences, +and retained failures. No speed, fresh held-out, new MCP/directed-path, community +quality, or god-object diagnosis claim follows from this checkpoint. ## Next evidence to collect @@ -1754,8 +1826,8 @@ development reduction, not a new comparative result or a completed repair. those broader checks. 2. Extend source-proven loop/result/iterator inference to recover the remaining fd misses. Evaluate TypeScript identity independently of the bounded query - binding proof above. Address Java varargs - signature completeness and unresolved receiver forms with separate evidence. + binding proof above. Extend Java evidence beyond the corrected varargs + cases, including untyped method results and unresolved receiver forms. Keep exact build/source provenance for subsequent release comparisons; the latest query correction has native and fixed-graph regression evidence. diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index 1105ff6a5..7c7f79826 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -118,7 +118,12 @@ collections: boxing/unboxing, array, complete source-hierarchy, and stable core-Java conversions, but only when one applicable vector is more specific than all other applicable vectors. Unknown hierarchy or a competing conversion - remains unresolved. + remains unresolved. Java checks strict fixed-arity applicability before + boxing/unboxing, and both fixed-arity phases before varargs expansion. A + spread declaration participates in fixed-arity phases with its declared + array type. Expanded specificity requires comparable parameter vectors; + differing fixed-prefix lengths remain ambiguous. This bounded source model + does not implement compiler-level generic inference. The TypeScript/JavaScript producer represents a tagged template (``tag`text ${value}``) as a call with occurrence context `tagged_template` (or `tagged_member` for a member tag). Its bounded argument From 232608eedb2aa2dd4134954c63b62bd46188a1d6 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:19:59 -0700 Subject: [PATCH 39/97] test: freeze source-backed responsibility explanation questions --- .../responsibility_questions_panel_a.json | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 benchmarks/agent_query/responsibility_questions_panel_a.json diff --git a/benchmarks/agent_query/responsibility_questions_panel_a.json b/benchmarks/agent_query/responsibility_questions_panel_a.json new file mode 100644 index 000000000..cc9352035 --- /dev/null +++ b/benchmarks/agent_query/responsibility_questions_panel_a.json @@ -0,0 +1,333 @@ +{ + "schema": "compass.responsibility-questions/1", + "scope": "Source-first questions for a new development arm on previously evaluated repositories and frozen graphs. Prior unrelated outputs were observed; this is not held-out or representative evidence.", + "sourceRun": "java-varargs-panel-a-02", + "sourceRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "protocol": { + "operation": "Both tools receive the identical natural-language query through their public query CLI.", + "budgetTokens": 2000, + "maxFollowUps": 0, + "timeoutSeconds": 120, + "sourceWorkingDirectory": true, + "nativeOnly": true, + "scoring": "Review all 20 facts for explicit correct answers and separately for sufficient returned source evidence. A symbol or neighbor name alone earns neither. Count omissions and unsupported asserted claims; raw source excerpts are evidence, not claims authored by the tool. Preserve ambiguity, truncation, errors and all failures. No score may establish god-object detection or functional community quality.", + "limitations": [ + "Purposive five-case development selection, one per language; no population precision estimate.", + "Shared stored graphs from a prior run; no new extraction or build-time comparison.", + "No model-assisted answers or independent semantic reviewer.", + "Degree, method counts and responsibility lists do not prove a god-object defect." + ] + }, + "cases": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "symbol": "Mux", + "question": "Explain how Mux coordinates routing, middleware and subrouters. What state is shared by With?", + "file": "mux.go", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "graphSha256": { + "compass": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphify": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + "facts": [ + { + "id": "chi-1", + "claim": "With creates an inline Mux that shares its parent pool and routing tree, while building a middleware slice for the inline router.", + "witnesses": [ + { + "startLine": 242, + "endLine": 262, + "text": "func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {\n\t// Similarly as in handle(), we must build the mux handler once additional\n\t// middleware registration isn't allowed for this stack, like now.\n\tif !mx.inline && mx.handler == nil {\n\t\tmx.updateRouteHandler()\n\t}\n\n\t// Copy middlewares from parent inline muxs\n\tvar mws Middlewares\n\tif mx.inline {\n\t\tmws = make(Middlewares, len(mx.middlewares))\n\t\tcopy(mws, mx.middlewares)\n\t}\n\tmws = append(mws, middlewares...)\n\n\tim := &Mux{\n\t\tpool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws,\n\t\tnotFoundHandler: mx.notFoundHandler, methodNotAllowedHandler: mx.methodNotAllowedHandler,\n\t}\n\n\treturn im" + } + ] + }, + { + "id": "chi-2", + "claim": "Use rejects middleware registration once the computed handler has been established.", + "witnesses": [ + { + "startLine": 100, + "endLine": 106, + "text": "func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) {\n\tif mx.handler != nil {\n\t\tpanic(\"chi: all middlewares must be defined before routes on a mux\")\n\t}\n\tmx.middlewares = append(mx.middlewares, middlewares...)\n}\n" + } + ] + }, + { + "id": "chi-3", + "claim": "Route constructs a new router, invokes the supplied configuration callback, and mounts that router at the pattern.", + "witnesses": [ + { + "startLine": 278, + "endLine": 287, + "text": "func (mx *Mux) Route(pattern string, fn func(r Router)) Router {\n\tif fn == nil {\n\t\tpanic(fmt.Sprintf(\"chi: attempting to Route() a nil subrouter on '%s'\", pattern))\n\t}\n\tsubRouter := NewRouter()\n\tfn(subRouter)\n\tmx.Mount(pattern, subRouter)\n\treturn subRouter\n}\n" + } + ] + }, + { + "id": "chi-4", + "claim": "routeHTTP looks up the request method and path in the route tree and invokes the selected HTTP handler when one is found.", + "witnesses": [ + { + "startLine": 476, + "endLine": 493, + "text": "\tmethod, ok := methodMap[rctx.RouteMethod]\n\tif !ok {\n\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)\n\t\treturn\n\t}\n\n\t// Find the route\n\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {\n\t\t// Set http.Request path values from our request context\n\t\tfor i, key := range rctx.URLParams.Keys {\n\t\t\tvalue := rctx.URLParams.Values[i]\n\t\t\tr.SetPathValue(key, value)\n\t\t}\n\t\tr.Pattern = rctx.RoutePattern()\n\n\t\th.ServeHTTP(w, r)\n\t\treturn\n\t}" + } + ] + } + ] + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "symbol": "_AtomicFile", + "question": "Explain _AtomicFile ownership, close behavior and context manager cleanup on exceptions.", + "file": "src/click/_compat.py", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "graphSha256": { + "compass": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphify": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + "facts": [ + { + "id": "click-1", + "claim": "The wrapper stores the file object plus temporary and real filenames; its name property returns the real filename.", + "witnesses": [ + { + "startLine": 455, + "endLine": 464, + "text": "class _AtomicFile:\n def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None:\n self._f = f\n self._tmp_filename = tmp_filename\n self._real_filename = real_filename\n self.closed = False\n\n @property\n def name(self) -> str:\n return self._real_filename" + } + ] + }, + { + "id": "click-2", + "claim": "On its first successful close, it closes the wrapped stream, replaces the real filename with the temporary filename, and marks itself closed. Later close calls return immediately.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "text": " def close(self, delete: bool = False) -> None:\n if self.closed:\n return\n self._f.close()\n os.replace(self._tmp_filename, self._real_filename)\n self.closed = True" + } + ] + }, + { + "id": "click-3", + "claim": "Unknown attribute access is forwarded to the wrapped stream.", + "witnesses": [ + { + "startLine": 473, + "endLine": 474, + "text": " def __getattr__(self, name: str) -> t.Any:\n return getattr(self._f, name)" + } + ] + }, + { + "id": "click-4", + "claim": "For an as-yet-open wrapper, __exit__ passes whether an exception occurred as delete, but close does not inspect delete: it still attempts replacement on exception, unless an earlier operation fails. Do not infer rollback from the parameter name.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "text": " def close(self, delete: bool = False) -> None:\n if self.closed:\n return\n self._f.close()\n os.replace(self._tmp_filename, self._real_filename)\n self.closed = True" + }, + { + "startLine": 479, + "endLine": 485, + "text": " def __exit__(\n self,\n exc_type: type[BaseException] | None,\n exc_value: BaseException | None,\n tb: TracebackType | None,\n ) -> None:\n self.close(delete=exc_type is not None)" + } + ] + } + ] + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "symbol": "Cleaner", + "question": "Explain Cleaner responsibilities, how it uses the supplied Safelist, and whether cleaning mutates the input document.", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "graphSha256": { + "compass": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "graphify": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + "facts": [ + { + "id": "jsoup-1", + "claim": "The constructor keeps the supplied Safelist reference directly after validating it, rather than making a copy.", + "witnesses": [ + { + "startLine": 50, + "endLine": 53, + "text": " public Cleaner(Safelist safelist) {\n Validate.notNull(safelist);\n this.safelist = safelist;\n }" + } + ] + }, + { + "id": "jsoup-2", + "claim": "clean builds a separate document shell, copies safe body nodes into it and clones the input output settings.", + "witnesses": [ + { + "startLine": 62, + "endLine": 70, + "text": " public Document clean(Document dirtyDocument) {\n Validate.notNull(dirtyDocument);\n\n Document clean = Document.createShell(dirtyDocument.baseUri());\n copySafeNodes(dirtyDocument.body(), clean.body());\n clean.outputSettings(dirtyDocument.outputSettings().clone());\n\n return clean;\n }" + } + ] + }, + { + "id": "jsoup-3", + "claim": "The cleaning visitor gates element copying on safe tags, and createSafeElement filters source attributes through the Safelist.", + "witnesses": [ + { + "startLine": 148, + "endLine": 158, + "text": " @Override public void head(Node source, int depth) {\n if (source instanceof Element) {\n Element sourceEl = (Element) source;\n\n if (safelist.isSafeTag(sourceEl.normalName())) { // safe, clone and copy safe attrs\n ElementMeta meta = createSafeElement(sourceEl);\n Element destChild = meta.el;\n destination.appendChild(destChild);\n\n numDiscarded += meta.numAttribsDiscarded;\n destination = destChild;" + }, + { + "startLine": 188, + "endLine": 209, + "text": " private ElementMeta createSafeElement(Element sourceEl) {\n Element dest = sourceEl.shallowClone(); // reuses tag, clones attributes and preserves any user data\n String sourceTag = sourceEl.tagName();\n Attributes destAttrs = dest.attributes();\n dest.clearAttributes(); // clear all non-internal attributes, ready for safe copy\n\n int numDiscarded = 0;\n Attributes sourceAttrs = sourceEl.attributes();\n for (Attribute sourceAttr : sourceAttrs) {\n if (safelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) { // will keep this attr\n String key = sourceAttr.getKey();\n String value = sourceAttr.getValue();\n\n if (safelist.shouldAbsUrl(sourceTag, key)) { // configured to make absolute urls for this key (href)\n value = sourceEl.absUrl(key);\n if (value.isEmpty()) // could not be made abs; leave as-is to allow custom unknown protocols\n value = sourceAttr.getValue();\n }\n Range.AttributeRange range = sourceAttrs.sourceRange(key);\n destAttrs.put(key, value);\n NodeInternals.attributeRange(destAttrs, key, range);\n } else" + } + ] + }, + { + "id": "jsoup-4", + "claim": "isValidBodyHtml parses a fragment and returns true only when no nodes or attributes were discarded and the tracked parse-error list is empty.", + "witnesses": [ + { + "startLine": 124, + "endLine": 133, + "text": " public boolean isValidBodyHtml(String bodyHtml) {\n String baseUri = (safelist.preserveRelativeLinks()) ? DummyUri : \"\"; // fake base URI to allow relative URLs to remain valid\n Document clean = Document.createShell(baseUri);\n Document dirty = Document.createShell(baseUri);\n ParseErrorList errorList = ParseErrorList.tracking(1);\n List nodes = Parser.parseFragment(bodyHtml, dirty.body(), baseUri, errorList);\n dirty.body().insertChildren(0, nodes);\n int numDiscarded = copySafeNodes(dirty.body(), clean.body());\n return numDiscarded == 0 && errorList.isEmpty();\n }" + } + ] + } + ] + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "symbol": "createStore", + "question": "Explain createStore responsibilities and how dispatch and subscriptions share state safely.", + "file": "src/createStore.ts", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "graphSha256": { + "compass": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphify": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + "facts": [ + { + "id": "redux-1", + "claim": "When an enhancer is supplied and validated, creation delegates to enhancer(createStore) with the reducer and preloaded state.", + "witnesses": [ + { + "startLine": 121, + "endLine": 134, + "text": " if (typeof enhancer !== 'undefined') {\n if (typeof enhancer !== 'function') {\n throw new Error(\n `Expected the enhancer to be a function. Instead, received: '${kindOf(\n enhancer\n )}'`\n )\n }\n\n return enhancer(createStore)(\n reducer,\n preloadedState as PreloadedState | undefined\n )\n }" + } + ] + }, + { + "id": "redux-2", + "claim": "dispatch runs the current reducer to update currentState while isDispatching is true, resetting the flag in finally.", + "witnesses": [ + { + "startLine": 293, + "endLine": 302, + "text": " if (isDispatching) {\n throw new Error('Reducers may not dispatch actions.')\n }\n\n try {\n isDispatching = true\n currentState = currentReducer(currentState, action)\n } finally {\n isDispatching = false\n }" + } + ] + }, + { + "id": "redux-3", + "claim": "Listener changes use a copy of currentListeners when needed; dispatch iterates its captured listener map, so mutations to nextListeners do not rewrite that in-progress snapshot.", + "witnesses": [ + { + "startLine": 152, + "endLine": 159, + "text": " function ensureCanMutateNextListeners() {\n if (nextListeners === currentListeners) {\n nextListeners = new Map()\n currentListeners.forEach((listener, key) => {\n nextListeners.set(key, listener)\n })\n }\n }" + }, + { + "startLine": 221, + "endLine": 243, + "text": " ensureCanMutateNextListeners()\n const listenerId = listenerIdCounter++\n nextListeners.set(listenerId, listener)\n\n return function unsubscribe() {\n if (!isSubscribed) {\n return\n }\n\n if (isDispatching) {\n throw new Error(\n 'You may not unsubscribe from a store listener while the reducer is executing. ' +\n 'See https://redux.js.org/api/store#subscribelistener for more details.'\n )\n }\n\n isSubscribed = false\n\n ensureCanMutateNextListeners()\n nextListeners.delete(listenerId)\n currentListeners = null\n }\n }" + }, + { + "startLine": 304, + "endLine": 307, + "text": " const listeners = (currentListeners = nextListeners)\n listeners.forEach(listener => {\n listener()\n })" + } + ] + }, + { + "id": "redux-4", + "claim": "The store exposes dispatch, subscribe, getState and replaceReducer, plus an observable adapter that reads state and uses the same subscription mechanism.", + "witnesses": [ + { + "startLine": 344, + "endLine": 394, + "text": " function observable() {\n const outerSubscribe = subscribe\n return {\n /**\n * The minimal observable subscription method.\n * @param observer Any object that can be used as an observer.\n * The observer object should have a `next` method.\n * @returns An object with an `unsubscribe` method that can\n * be used to unsubscribe the observable from the store, and prevent further\n * emission of values from the observable.\n */\n subscribe(observer: unknown) {\n if (typeof observer !== 'object' || observer === null) {\n throw new TypeError(\n `Expected the observer to be an object. Instead, received: '${kindOf(\n observer\n )}'`\n )\n }\n\n function observeState() {\n const observerAsObserver = observer as Observer\n if (observerAsObserver.next) {\n observerAsObserver.next(getState())\n }\n }\n\n observeState()\n const unsubscribe = outerSubscribe(observeState)\n return { unsubscribe }\n },\n\n [$$observable]() {\n return this\n }\n }\n }\n\n // When a store is created, an \"INIT\" action is dispatched so that every\n // reducer returns their initial state. This effectively populates\n // the initial state tree.\n dispatch({ type: ActionTypes.INIT } as A)\n\n const store = {\n dispatch: dispatch as Dispatch
,\n subscribe,\n getState,\n replaceReducer,\n [$$observable]: observable\n } as unknown as Store & Ext\n return store" + } + ] + } + ] + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "symbol": "IntoIter", + "question": "Explain IntoIter responsibilities, how it limits open directories, and how it detects symlink loops.", + "file": "src/lib.rs", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "graphSha256": { + "compass": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphify": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + }, + "facts": [ + { + "id": "walkdir-1", + "claim": "IntoIter next traverses its directory stack, prunes beyond max_depth and returns individual directory-reading errors as Some(Err).", + "witnesses": [ + { + "startLine": 699, + "endLine": 724, + "text": " while !self.stack_list.is_empty() {\n self.depth = self.stack_list.len();\n if let Some(dentry) = self.get_deferred_dir() {\n return Some(Ok(dentry));\n }\n if self.depth > self.opts.max_depth {\n // If we've exceeded the max depth, pop the current dir\n // so that we don't descend.\n self.pop();\n continue;\n }\n // Unwrap is safe here because we've verified above that\n // `self.stack_list` is not empty\n let next = self\n .stack_list\n .last_mut()\n .expect(\"BUG: stack should be non-empty\")\n .next();\n match next {\n None => self.pop(),\n Some(Err(err)) => return Some(Err(err)),\n Some(Ok(dent)) => {\n if let Some(result) = self.handle_entry(dent) {\n return Some(result);\n }\n }" + } + ] + }, + { + "id": "walkdir-2", + "claim": "push closes the oldest open directory stream when max_open is reached before opening another directory.", + "witnesses": [ + { + "startLine": 901, + "endLine": 911, + "text": " fn push(&mut self, dent: &DirEntry) -> Result<()> {\n // Make room for another open file descriptor if we've hit the max.\n let free =\n self.stack_list.len().checked_sub(self.oldest_opened).unwrap();\n if free == self.opts.max_open {\n self.stack_list[self.oldest_opened].close();\n }\n // Open a handle to reading the directory's entries.\n let rd = fs::read_dir(dent.path()).map_err(|err| {\n Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))\n });" + } + ] + }, + { + "id": "walkdir-3", + "claim": "Following a symlink invokes loop checking for directory targets; check_loop compares the target handle with ancestor handles and returns a loop error on a match.", + "witnesses": [ + { + "startLine": 961, + "endLine": 989, + "text": " fn follow(&self, mut dent: DirEntry) -> Result {\n dent =\n DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;\n // The only way a symlink can cause a loop is if it points\n // to a directory. Otherwise, it always points to a leaf\n // and we can omit any loop checks.\n if dent.is_dir() {\n self.check_loop(dent.path())?;\n }\n Ok(dent)\n }\n\n fn check_loop>(&self, child: P) -> Result<()> {\n let hchild = Handle::from_path(&child)\n .map_err(|err| Error::from_io(self.depth, err))?;\n for ancestor in self.stack_path.iter().rev() {\n let is_same = ancestor\n .is_same(&hchild)\n .map_err(|err| Error::from_io(self.depth, err))?;\n if is_same {\n return Err(Error::from_loop(\n self.depth,\n &ancestor.path,\n child.as_ref(),\n ));\n }\n }\n Ok(())\n }" + } + ] + }, + { + "id": "walkdir-4", + "claim": "With contents_first enabled, directory entries are deferred and later returned after traversal of their contents, subject to depth filtering.", + "witnesses": [ + { + "startLine": 873, + "endLine": 898, + "text": " }\n if is_normal_dir && self.opts.contents_first {\n self.deferred_dirs.push(dent);\n None\n } else if self.skippable() {\n None\n } else {\n Some(Ok(dent))\n }\n }\n\n fn get_deferred_dir(&mut self) -> Option {\n if self.opts.contents_first {\n if self.depth < self.deferred_dirs.len() {\n // Unwrap is safe here because we've guaranteed that\n // `self.deferred_dirs.len()` can never be less than 1\n let deferred: DirEntry = self\n .deferred_dirs\n .pop()\n .expect(\"BUG: deferred_dirs should be non-empty\");\n if !self.skippable() {\n return Some(deferred);\n }\n }\n }\n None" + } + ] + } + ] + } + ] +} From 964a62573cbc4a5e4c2e82896568cfcba95ea03b Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:27:36 -0700 Subject: [PATCH 40/97] test: record responsibility query gaps and final qualification --- benchmarks/agent_query/COVERAGE_PLAN.md | 12 + .../java_varargs_development_review.json | 19 +- .../responsibility_review_panel_a.json | 491 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 51 +- 4 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 benchmarks/agent_query/responsibility_review_panel_a.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 7eef88257..d2c94a69c 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -112,6 +112,18 @@ summary is unavailable in that response, not an incorrect answer; neither a neighbor follow-up workflow nor Graphify's separate CLI is excluded by this finding. Do not turn summary availability into a cross-tool accuracy score. +### Responsibility explanation evidence + +`responsibility_questions_panel_a.json` freezes five questions and 20 source +facts in commit `232608ee`. The first arm uses identical natural queries, a +requested 2,000-token budget and no follow-ups on the paired final Java graphs. +`responsibility_review_panel_a.json` records full-fact coverage separately from +explicit answers. Partial graph facts and useful source locations do not imply +the complete implementation mechanism. Extra graph assertions require their +own source review before any precision claim. Equal requested budgets do not +imply equal actual output sizes. This reused-repository development arm is not +a god-object oracle; a symmetric source-reading workflow remains necessary. + ### MCP path diagnostics `suite_mcp_paths.json` covers prepared exact-ID navigation, reverse traversal, diff --git a/benchmarks/agent_query/java_varargs_development_review.json b/benchmarks/agent_query/java_varargs_development_review.json index fa7317151..5184c5be5 100644 --- a/benchmarks/agent_query/java_varargs_development_review.json +++ b/benchmarks/agent_query/java_varargs_development_review.json @@ -957,7 +957,7 @@ }, "clippyWorkspaceAndFocusedTests": "passed -D warnings", "formatDiffAndProductBoundary": "passed", - "fullFixtureQualification": "running against final production sources", + "fullFixtureQualification": "passed final-source fixtures-only qualification; retry after interrupted process was confirmed absent", "logsSha256": { "java-varargs-diagnostic/native-before.log": "12e803e1b59043f0b96f32c90c6fa43604fff9eef11ad8fa2df11a52d48cfeb7", "java-varargs-diagnostic/resolve-before.log": "5a1ca619ecb0675cef3e74616e159da654fa8c444b2ddc76d2a7414b0f0c85bc", @@ -977,7 +977,8 @@ "java-varargs-resolution-02/clippy-tests.log": "fac2c1f1a82ac72753325b2f827dc4175737176ce2e24891453446a5c3e5aba5", "java-varargs-resolution-02/build-01.log": "417587ddafc7f98dcabb9a1e2a27c82e820f932ffa72d1bc535b88b7ad1fc735", "java-varargs-resolution-02/array-receiver-native.log": "5bfc3defd7d1840c09033831db91e5acd305ab0f400c31be7ee5d407a3da2a39", - "java-varargs-resolution-02/array-receiver-clippy.log": "44af7ac5bc4dcde7a22e573735545e22d984787c7ac702a061db4bd5f2142562" + "java-varargs-resolution-02/array-receiver-clippy.log": "44af7ac5bc4dcde7a22e573735545e22d984787c7ac702a061db4bd5f2142562", + "java-varargs-resolution-02/full-qualification-02.log": "f3c8f86f610855ef9609782d356bee868df87f10a9b516728b1a74899097f289" }, "additionalArrayReceiverRegression": { "status": "passed; spread, ordinary-array, and trailing-dimension parameters retain unresolved clone occurrences without an invented element/array type receiver", @@ -997,6 +998,20 @@ "logSha256": "92cc06186a8584921cb7faddc60d8abd060c8a4be2bd731ad6c7d20b7dc9a4a7", "releaseBinarySha256": "6d6c0a4caddf791ecd669367535f153929e8e95ef2f571cacdc5fdbeff20fe5f", "scope": "Superseded run: started before the late zero-argument missing-types guard and ended after that edit. Retained as diagnostic history; not final-source qualification evidence." + }, + "interruptedFinalQualification": { + "status": "interrupted; handle missing and no matching process remained after turn interruption", + "logSha256": "5db7d8616f7238454b9a3353cc44fb604fd8f5a8797265b80ff8160b834fa4d5", + "compilerSampleSha256": "82cb63586712db0208775b33be375afb6cb17e7dfd428d483b69c422c003952c", + "scope": "Initial final-source attempt completed semantic/topology checks but stopped during release compilation. Not counted as a complete qualification." + }, + "completedFinalQualification": { + "exitCode": 0, + "log": "java-varargs-resolution-02/full-qualification-02.log", + "logSha256": "f3c8f86f610855ef9609782d356bee868df87f10a9b516728b1a74899097f289", + "releaseBinarySha256": "b2f4b9d6331b6f45319ca7e7b19caa5d8a43ffa5c9e5f287902742e4ca8c65b3", + "productionSourcesMatchFrozenComparison": true, + "scope": "Complete fixtures-only gate including semantic assertions, topology, independent Markdown checks, release frontend precedence/activation/determinism and source-anchor checks." } }, "limitations": [ diff --git a/benchmarks/agent_query/responsibility_review_panel_a.json b/benchmarks/agent_query/responsibility_review_panel_a.json new file mode 100644 index 000000000..7e2381d04 --- /dev/null +++ b/benchmarks/agent_query/responsibility_review_panel_a.json @@ -0,0 +1,491 @@ +{ + "schema": "compass.responsibility-review/1", + "scope": "Manual review of completeness for 20 frozen source facts across five native natural-query responses per tool. It is not a complete precision audit of every returned node/edge, a held-out result, or a god-object diagnosis evaluation.", + "questionCommit": "232608ee", + "questionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "captureSha256": "99ac02e229d30b34d7bf3256ef4b9e46084e14b65efbdaa88b47160690c48d6f", + "collectorSha256": "18089c5b11461f2f4d9ba38b16d369ae7c02d5e38dc8f2b1e0f4e8ba92bfdc63", + "runnerSha256": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "artifactRoot": "responsibility-explanation-01", + "metrics": { + "compass": { + "requests": 5, + "successfulExecutions": 5, + "explicitCorrectFacts": 0, + "sufficientEvidenceFacts": 0, + "totalFacts": 20, + "stdoutBytes": 39740, + "requestedTokensPerRequest": 2000 + }, + "graphify": { + "requests": 5, + "successfulExecutions": 5, + "explicitCorrectFacts": 0, + "sufficientEvidenceFacts": 0, + "totalFacts": 20, + "stdoutBytes": 53306, + "requestedTokensPerRequest": 2000 + } + }, + "reviews": [ + { + "repository": "chi", + "tool": "compass", + "stdoutSha256": "37657db8665dda901e0e3befbd557c0987c833bc18c6e6722ae004b7c4e27bbf", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stdoutBytes": 7949, + "byteDivFourTokenEstimate": 1988, + "responseStatus": "RESULT needs_resolution \u00b7 match=ambiguous \u00b7 execution=partial \u00b7 coverage=unknown", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 64, + "edgeRows": 44, + "factReviews": [ + { + "id": "chi-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Names and call edges do not show shared pool/tree fields or middleware slice construction." + }, + { + "id": "chi-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No handler guard or rejection behavior is shown." + }, + { + "id": "chi-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete construct/configure/mount sequence is shown." + }, + { + "id": "chi-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No conditional route lookup and matched-handler invocation is shown." + } + ] + }, + { + "repository": "chi", + "tool": "graphify", + "stdoutSha256": "1bf6c1ffd510f5a1183367edaba6fb6a7a66764cd9a658fdaa9cfa3eeff6b52f", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "stdoutBytes": 6571, + "byteDivFourTokenEstimate": 1643, + "responseStatus": "graph traversal context", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 101, + "edgeRows": 0, + "factReviews": [ + { + "id": "chi-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Names and call edges do not show shared pool/tree fields or middleware slice construction." + }, + { + "id": "chi-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No handler guard or rejection behavior is shown." + }, + { + "id": "chi-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete construct/configure/mount sequence is shown." + }, + { + "id": "chi-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No conditional route lookup and matched-handler invocation is shown." + } + ] + }, + { + "repository": "click", + "tool": "compass", + "stdoutSha256": "09d1dab6c20017246f8e36ffe47f45c995cdf3b8f9d2e4fa9717f843769cdaf3", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stdoutBytes": 7868, + "byteDivFourTokenEstimate": 1967, + "responseStatus": "RESULT candidates \u00b7 match=fuzzy \u00b7 execution=partial \u00b7 coverage=unknown", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 64, + "edgeRows": 10, + "factReviews": [ + { + "id": "click-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No stored filenames or name property body is shown." + }, + { + "id": "click-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No close guard, stream close, replace and closed-state sequence is shown." + }, + { + "id": "click-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No __getattr__ forwarding implementation is shown." + }, + { + "id": "click-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete close/__exit__ bodies establish the ignored delete flag." + } + ] + }, + { + "repository": "click", + "tool": "graphify", + "stdoutSha256": "f8e7ae3aae06a943c686f3cd5f94cc7c7ea1f7da243ef8aa1ff35b5984564da5", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "stdoutBytes": 6850, + "byteDivFourTokenEstimate": 1713, + "responseStatus": "graph traversal context", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 87, + "edgeRows": 0, + "factReviews": [ + { + "id": "click-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No stored filenames or name property body is shown." + }, + { + "id": "click-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No close guard, stream close, replace and closed-state sequence is shown." + }, + { + "id": "click-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No __getattr__ forwarding implementation is shown." + }, + { + "id": "click-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete close/__exit__ bodies establish the ignored delete flag." + } + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "stdoutSha256": "51bcb6e5217ff0bb974a18fb07a6b636d7a2b67c2ed5641c5da5ad1acf6c5a94", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stdoutBytes": 7938, + "byteDivFourTokenEstimate": 1985, + "responseStatus": "RESULT needs_resolution \u00b7 match=ambiguous \u00b7 execution=partial \u00b7 coverage=incomplete", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 61, + "edgeRows": 0, + "factReviews": [ + { + "id": "jsoup-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No constructor assignment establishes reference sharing." + }, + { + "id": "jsoup-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete new-shell/copy/output-settings-clone sequence is shown." + }, + { + "id": "jsoup-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No safe-tag and safe-attribute predicates are shown." + }, + { + "id": "jsoup-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No parsing and combined discarded-count/error-list condition is shown." + } + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "stdoutSha256": "21db49e7171f06bfa3b36559f63a622705673ca94869932c49389803f09829c2", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "stdoutBytes": 6667, + "byteDivFourTokenEstimate": 1667, + "responseStatus": "graph traversal context", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 65, + "edgeRows": 0, + "factReviews": [ + { + "id": "jsoup-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No constructor assignment establishes reference sharing." + }, + { + "id": "jsoup-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete new-shell/copy/output-settings-clone sequence is shown." + }, + { + "id": "jsoup-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No safe-tag and safe-attribute predicates are shown." + }, + { + "id": "jsoup-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No parsing and combined discarded-count/error-list condition is shown." + } + ] + }, + { + "repository": "redux", + "tool": "compass", + "stdoutSha256": "d30483e834045767618774140ade72fac87144bd43938a2360ab005ba9681235", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stdoutBytes": 7997, + "byteDivFourTokenEstimate": 2000, + "responseStatus": "RESULT needs_resolution \u00b7 match=ambiguous \u00b7 execution=partial \u00b7 coverage=incomplete", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 64, + "edgeRows": 16, + "factReviews": [ + { + "id": "redux-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No enhancer validation and invocation chain is shown." + }, + { + "id": "redux-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No reducer update and finally-reset sequence is shown." + }, + { + "id": "redux-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No copy-on-write plus captured listener iteration is shown." + }, + { + "id": "redux-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete returned store and observable adapter implementation is shown." + } + ] + }, + { + "repository": "redux", + "tool": "graphify", + "stdoutSha256": "ba6c21a04ee4f7d720ee74726dd4551ce982429d97ade961ce50db31ef9a58da", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "stdoutBytes": 6609, + "byteDivFourTokenEstimate": 1653, + "responseStatus": "graph traversal context", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 81, + "edgeRows": 0, + "factReviews": [ + { + "id": "redux-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No enhancer validation and invocation chain is shown." + }, + { + "id": "redux-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No reducer update and finally-reset sequence is shown." + }, + { + "id": "redux-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No copy-on-write plus captured listener iteration is shown." + }, + { + "id": "redux-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "No complete returned store and observable adapter implementation is shown." + } + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "stdoutSha256": "4e0658ae31900521385690c31083b617977c322a9af823faf5f17505ad63be49", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stdoutBytes": 7988, + "byteDivFourTokenEstimate": 1997, + "responseStatus": "RESULT needs_resolution \u00b7 match=ambiguous \u00b7 execution=partial \u00b7 coverage=unknown", + "truncationDisclosed": true, + "overBudgetDisclosed": false, + "nodeRows": 64, + "edgeRows": 20, + "factReviews": [ + { + "id": "walkdir-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Types and method edges do not establish stack iteration, depth guard and error-return behavior." + }, + { + "id": "walkdir-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "A push-to-close edge does not establish max_open, oldest-stream selection or close-before-open ordering." + }, + { + "id": "walkdir-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Call edges do not establish the directory-only guard, ancestor comparison or error condition." + }, + { + "id": "walkdir-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Method edges do not establish contents_first and depth predicates or deferred-entry ordering." + } + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "stdoutSha256": "729cd0be188021d0e815f4167dd547728feb812a8a497f638f9164f86f3028be", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb", + "stdoutBytes": 26609, + "byteDivFourTokenEstimate": 6653, + "responseStatus": "graph traversal context", + "truncationDisclosed": false, + "overBudgetDisclosed": true, + "nodeRows": 111, + "edgeRows": 244, + "factReviews": [ + { + "id": "walkdir-1", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Types and method edges do not establish stack iteration, depth guard and error-return behavior." + }, + { + "id": "walkdir-2", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "A push-to-close edge does not establish max_open, oldest-stream selection or close-before-open ordering." + }, + { + "id": "walkdir-3", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Call edges do not establish the directory-only guard, ancestor comparison or error condition." + }, + { + "id": "walkdir-4", + "explicitCorrectAnswer": false, + "sufficientReturnedEvidence": false, + "reason": "Method edges do not establish contents_first and depth predicates or deferred-entry ordering." + } + ] + } + ], + "interpretation": [ + "Both products primarily return graph context in this arm. Omitted full facts are not fabricated answers; useful declaration locations and partial graph evidence remain present.", + "The single-request 2000-token arm does not evaluate source-reading or disambiguation follow-ups. Compass explain advertises digest-verified source; Graphify explain/get_node or an agent source-reading workflow must be assessed separately rather than ruled out from query output.", + "Graphify WalkDir explicitly reports a complete answer over budget. Its full 26609-byte response is retained and reviewed, not silently clipped. Equal requested budgets did not produce equal output size. Byte-divided-by-four estimates are evaluator accounting, not a claim about either tool internal estimator.", + "Four Compass responses request resolution; the Click response reports candidates. This is incomplete task evidence, not a successful guess.", + "No wrong task-level prose assertion was identified because these responses do not assert the 20 implementation mechanisms. Extra node and edge assertions are not exhaustively source-reviewed; precision remains unmeasured." + ], + "postOutputDiagnostics": [ + { + "repository": "walkdir", + "tool": "graphify", + "classification": "wrong internal call target", + "source": "src_lib_intoiter_get_deferred_dir", + "target": "src_lib_intoiter_pop", + "reportedSite": "src/lib.rs:L889", + "actualReceiver": "self.deferred_dirs, declared Vec at src/lib.rs:598", + "targetDefinition": "IntoIter::pop at src/lib.rs:950 mutates IntoIter stacks; it is not Vec::pop", + "explanation": "The displayed edge assigns the Vec receiver call inside get_deferred_dir to a same-named method on the enclosing IntoIter. The source declares the receiver field as Vec.", + "compassObservation": "No corresponding edge to IntoIter::pop; Compass retains the source-supported get_deferred_dir -> skippable call. Absence of the wrong internal edge does not recover or score the external Vec call.", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "graphSha256": { + "compass": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphify": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + }, + "scope": "Selected after output inspection; one diagnosed false target, not a comparative precision estimate.", + "sourceWitnesses": [ + { + "file": "src/lib.rs", + "startLine": 598, + "endLine": 598, + "text": " deferred_dirs: Vec," + }, + { + "file": "src/lib.rs", + "startLine": 884, + "endLine": 898, + "text": " fn get_deferred_dir(&mut self) -> Option {\n if self.opts.contents_first {\n if self.depth < self.deferred_dirs.len() {\n // Unwrap is safe here because we've guaranteed that\n // `self.deferred_dirs.len()` can never be less than 1\n let deferred: DirEntry = self\n .deferred_dirs\n .pop()\n .expect(\"BUG: deferred_dirs should be non-empty\");\n if !self.skippable() {\n return Some(deferred);\n }\n }\n }\n None" + }, + { + "file": "src/lib.rs", + "startLine": 950, + "endLine": 959, + "text": " fn pop(&mut self) {\n self.stack_list.pop().expect(\"BUG: cannot pop from empty stack\");\n if self.opts.follow_links {\n self.stack_path.pop().expect(\"BUG: list/path stacks out of sync\");\n }\n // If everything in the stack is already closed, then there is\n // room for at least one more open descriptor and it will\n // always be at the top of the stack.\n self.oldest_opened = min(self.oldest_opened, self.stack_list.len());\n }" + } + ], + "wrongStoredEdge": { + "source": "src_lib_intoiter_get_deferred_dir", + "target": "src_lib_intoiter_pop", + "relation": "calls", + "_origin": "ast", + "confidence": "EXTRACTED", + "confidence_score": 1.0, + "context": "call", + "source_file": "src/lib.rs", + "source_location": "L889", + "weight": 1.0 + } + } + ], + "remainingWork": [ + "Review additional graph assertions, starting with the complete 244-edge WalkDir response, before any precision claim.", + "Evaluate a symmetric bounded source-reading/disambiguation workflow using only information returned by each tool.", + "Diagnose Compass owner specificity and answer-budget allocation against independent positive and negative cases.", + "Extend actual responsibility and community judgments beyond the small Chi pilot and include plausible positive cases and independent review." + ], + "limitations": [ + "The 20 frozen fact judgments are complete. Full source precision over every returned graph assertion is incomplete; the extra WalkDir diagnosis is a post-output case.", + "Top-level CLI help and installed Graphify get_node/explain implementation were inspected after the first query capture. This review does not claim that native query is intended to synthesize a prose explanation." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index f9e92c826..7bbc1b975 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1804,7 +1804,11 @@ Verification so far: 1,092 workspace library/binary tests passed with two ignored; 250 product/cache/resolution contract tests and nine focused tests passed; workspace and focused-test Clippy passed with `-D warnings`; format, diff, product-boundary, and all 99 benchmark harness tests passed. The full -fixture qualification is running against final production sources. Review found +fixture qualification passed against final production sources, including release +frontend precedence, activation, determinism, and source-anchor checks. The first +final-source attempt was interrupted during release compilation; its missing +handle and absent process were confirmed before retrying. Its partial log is +retained and is not counted as a full pass. Review found that a missing optional type vector could incorrectly prove a zero-parameter declaration; that regression failed before an added exact-match guard and passed afterward. The earlier fixture run passed but is superseded by that source @@ -1819,6 +1823,51 @@ records binaries, sources, graph hashes, all source judgments, cache differences and retained failures. No speed, fresh held-out, new MCP/directed-path, community quality, or god-object diagnosis claim follows from this checkpoint. +## Responsibility explanation evidence on the five-language panel + +Commit `232608ee` froze five natural-language questions and 20 source-backed +implementation facts before either tool answered those questions. This is a new +development arm on previously evaluated repositories, with the final Java +comparison graphs reused. It is not held-out or representative evidence. +Both tools received the same question, a requested 2,000-token budget, the +source checkout as their working directory, and no follow-up allowance. +All ten requests completed successfully. + +| Complete fact coverage in the returned response | Compass | Graphify | +| --- | ---: | ---: | +| Explicit correct implementation facts | 0/20 | 0/20 | +| Sufficient returned evidence for the full fact | 0/20 | 0/20 | +| Total stdout bytes across five requests | 39,740 | 53,306 | + +These native queries primarily return graph context. Names, navigation anchors, +and partial relationships remain useful, but they do not establish such facts +as shared router state, a file wrapper's exception behavior, listener snapshots, +or symlink-loop conditions. The result does not show fabricated prose answers; +there were no task-level mechanism assertions to score for prose precision. +Four Compass responses request resolution; the Click response returns +candidates. A source-reading/disambiguation workflow remains to be compared. +Compass `explain` advertises verified source; Graphify's other public operations +and a symmetric agent source-reading workflow are not excluded by this arm. + +Graphify's WalkDir response explicitly reports that its complete answer exceeds +the requested budget. All 26,609 bytes were retained, including 244 edge rows; +equal requested budgets did not produce equal answer sizes. No latency claim +is made while compilation shares the machine. + +Post-output inspection identifies one wrong Graphify call target: +`IntoIter::get_deferred_dir` is linked to `IntoIter::pop`, but the receiver at +that source occurrence is the `Vec` field `deferred_dirs`. Compass +keeps the valid call to `skippable` and has no corresponding wrong internal +`pop` edge. That does not recover the external vector call or establish overall +precision. The remaining additional node/edge assertions have not all been +source-reviewed; full response precision remains incomplete. + +The [frozen questions](../../benchmarks/agent_query/responsibility_questions_panel_a.json) +and [fact review](../../benchmarks/agent_query/responsibility_review_panel_a.json) +preserve exact source witnesses, raw-response hashes, all omissions, the budget +discrepancy, and the diagnosed edge. God-object responsibility judgments, +functional community quality and overall superiority remain unproven. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 62f60b298a7caed67429048565e33d8824c34225 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:30:43 -0700 Subject: [PATCH 41/97] test: freeze bounded source follow-up protocol --- ...esponsibility_source_followup_panel_a.json | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 benchmarks/agent_query/responsibility_source_followup_panel_a.json diff --git a/benchmarks/agent_query/responsibility_source_followup_panel_a.json b/benchmarks/agent_query/responsibility_source_followup_panel_a.json new file mode 100644 index 000000000..65ef05cc7 --- /dev/null +++ b/benchmarks/agent_query/responsibility_source_followup_panel_a.json @@ -0,0 +1,225 @@ +{ + "schema": "compass.responsibility-source-followup/1", + "scope": "Post-query development follow-up, selected and frozen before reading the bounded source windows. This measures an agent workflow using graph-returned anchors, not a native-only answer.", + "sourceQuestionCommit": "232608ee", + "inputCapture": "responsibility-explanation-01", + "policy": { + "allowedReadsPerResponse": 1, + "sourceBytesPerRead": 8000, + "maxSourceFileBytes": 4194304, + "selection": "Match the exact supplied subject against terminal displayed NODE names, ignoring the display-only () suffix. Exclude explicit file/module containers. All returned matching anchors are retained. Read only if they all identify one file; do not resolve a declaration ambiguity by picking one candidate.", + "window": "Read a contiguous byte window beginning at the earliest returned matching line in that one file. Retain all distinct declaration lines and disclose truncation; this window origin does not select a declaration as the answer.", + "oracleSeparation": "No graph-file lookup or source oracle range participates in selecting the file or window. Source facts are used only after capture for evidence judgment.", + "safety": "Require a repository-relative regular file contained beneath the pinned checkout, within the file-size bound, with a valid returned line. No mutations of input repositories.", + "scoring": "Rejudge the same 20 facts for sufficient combined query-plus-source evidence; distinguish observed code from a synthesized explanation. Record actual source bytes and initial response bytes separately. Preserve declaration ambiguity, omissions and incomplete reads.", + "limitations": [ + "A single contiguous source read is one bounded agent policy, not a best-possible workflow.", + "Window position and declaration multiplicity affect coverage. Further disambiguation, wider reads and native explain/get_node workflows remain outside this arm.", + "Neither equal requested query budgets nor this follow-up imply equal prior response sizes.", + "No god-object label, community quality judgment, latency claim or broad superiority conclusion follows." + ] + }, + "requests": [ + { + "repository": "chi", + "tool": "compass", + "subject": "Mux", + "inputSha256": "37657db8665dda901e0e3befbd557c0987c833bc18c6e6722ae004b7c4e27bbf", + "anchors": [ + { + "label": "chi.Mux", + "file": "mux.go", + "line": 21, + "kind": "struct", + "returnedRow": "NODE chi.Mux [struct] mux.go:21:5-48:1" + } + ], + "fileSelection": "unique-file", + "selectedFile": "mux.go", + "startLine": 21 + }, + { + "repository": "chi", + "tool": "graphify", + "subject": "Mux", + "inputSha256": "1bf6c1ffd510f5a1183367edaba6fb6a7a66764cd9a658fdaa9cfa3eeff6b52f", + "anchors": [ + { + "label": "Mux", + "file": "mux.go", + "line": 21, + "kind": "displayed-node", + "returnedRow": "NODE Mux [src=mux.go loc=L21 community=3]" + } + ], + "fileSelection": "unique-file", + "selectedFile": "mux.go", + "startLine": 21 + }, + { + "repository": "click", + "tool": "compass", + "subject": "_AtomicFile", + "inputSha256": "09d1dab6c20017246f8e36ffe47f45c995cdf3b8f9d2e4fa9717f843769cdaf3", + "anchors": [ + { + "label": "src.click._compat._AtomicFile", + "file": "src/click/_compat.py", + "line": 455, + "kind": "class", + "returnedRow": "NODE src.click._compat._AtomicFile [class] src/click/_compat.py:455:0-488:28" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/click/_compat.py", + "startLine": 455 + }, + { + "repository": "click", + "tool": "graphify", + "subject": "_AtomicFile", + "inputSha256": "f8e7ae3aae06a943c686f3cd5f94cc7c7ea1f7da243ef8aa1ff35b5984564da5", + "anchors": [ + { + "label": "_AtomicFile", + "file": "src/click/_compat.py", + "line": 455, + "kind": "displayed-node", + "returnedRow": "NODE _AtomicFile [src=src/click/_compat.py loc=L455 community=86]" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/click/_compat.py", + "startLine": 455 + }, + { + "repository": "jsoup", + "tool": "compass", + "subject": "Cleaner", + "inputSha256": "51bcb6e5217ff0bb974a18fb07a6b636d7a2b67c2ed5641c5da5ad1acf6c5a94", + "anchors": [ + { + "label": "org.jsoup.safety.Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 43, + "kind": "class", + "returnedRow": "NODE org.jsoup.safety.Cleaner [class] src/main/java/org/jsoup/safety/Cleaner.java:43:0-247:1" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43 + }, + { + "repository": "jsoup", + "tool": "graphify", + "subject": "Cleaner", + "inputSha256": "21db49e7171f06bfa3b36559f63a622705673ca94869932c49389803f09829c2", + "anchors": [ + { + "label": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 43, + "kind": "displayed-node", + "returnedRow": "NODE Cleaner [src=src/main/java/org/jsoup/safety/Cleaner.java loc=L43 community=129]" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43 + }, + { + "repository": "redux", + "tool": "compass", + "subject": "createStore", + "inputSha256": "d30483e834045767618774140ade72fac87144bd43938a2360ab005ba9681235", + "anchors": [ + { + "label": "createStore.createStore", + "file": "src/createStore.ts", + "line": 75, + "kind": "function", + "returnedRow": "NODE createStore.createStore [function] src/createStore.ts:75:7-85:61" + }, + { + "label": "createStore.createStore", + "file": "src/createStore.ts", + "line": 86, + "kind": "function", + "returnedRow": "NODE createStore.createStore [function] src/createStore.ts:86:7-395:1" + }, + { + "label": "createStore.createStore", + "file": "src/createStore.ts", + "line": 41, + "kind": "function", + "returnedRow": "NODE createStore.createStore [function] src/createStore.ts:41:7-49:61" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/createStore.ts", + "startLine": 41 + }, + { + "repository": "redux", + "tool": "graphify", + "subject": "createStore", + "inputSha256": "ba6c21a04ee4f7d720ee74726dd4551ce982429d97ade961ce50db31ef9a58da", + "anchors": [ + { + "label": "createStore()", + "file": "src/createStore.ts", + "line": 86, + "kind": "displayed-node", + "returnedRow": "NODE createStore() [src=src/createStore.ts loc=L86 community=25]" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/createStore.ts", + "startLine": 86 + }, + { + "repository": "walkdir", + "tool": "compass", + "subject": "IntoIter", + "inputSha256": "4e0658ae31900521385690c31083b617977c322a9af823faf5f17505ad63be49", + "anchors": [ + { + "label": "::IntoIter", + "file": "src/lib.rs", + "line": 538, + "kind": "type_alias", + "returnedRow": "NODE ::IntoIter [type_alias] src/lib.rs:538:4-538:29" + }, + { + "label": "walkdir::IntoIter", + "file": "src/lib.rs", + "line": 566, + "kind": "struct", + "returnedRow": "NODE walkdir::IntoIter [struct] src/lib.rs:566:0-606:1" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/lib.rs", + "startLine": 538 + }, + { + "repository": "walkdir", + "tool": "graphify", + "subject": "IntoIter", + "inputSha256": "729cd0be188021d0e815f4167dd547728feb812a8a497f638f9164f86f3028be", + "anchors": [ + { + "label": "IntoIter", + "file": "src/lib.rs", + "line": 566, + "kind": "displayed-node", + "returnedRow": "NODE IntoIter [src=src/lib.rs loc=L566 community=2]" + } + ], + "fileSelection": "unique-file", + "selectedFile": "src/lib.rs", + "startLine": 566 + } + ] +} From 0b3edddb41466bb813748a5fe57d40cb0d650acf Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:39:44 -0700 Subject: [PATCH 42/97] test: record symmetric source follow-up evidence coverage --- benchmarks/agent_query/COVERAGE_PLAN.md | 11 +- ...bility_source_followup_review_panel_a.json | 807 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 47 + 3 files changed, 864 insertions(+), 1 deletion(-) create mode 100644 benchmarks/agent_query/responsibility_source_followup_review_panel_a.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index d2c94a69c..2bad05460 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -122,7 +122,16 @@ explicit answers. Partial graph facts and useful source locations do not imply the complete implementation mechanism. Extra graph assertions require their own source review before any precision claim. Equal requested budgets do not imply equal actual output sizes. This reused-repository development arm is not -a god-object oracle; a symmetric source-reading workflow remains necessary. +a god-object oracle. + +`responsibility_source_followup_panel_a.json`, frozen in `62f60b29`, adds one +source window per response, selected only from returned exact-subject anchors. +The matching review records 11/20 sufficient facts for Compass and 13/20 for +Graphify, with 35,692 source bytes each. Graphify's Redux implementation anchor +provides two additional facts under the frozen earliest-anchor policy; Compass +also returns preceding overload declarations. Keep that policy effect and all +declaration ambiguities visible. This measures available source evidence, not +native explanation quality; further reading and disambiguation remain open. ### MCP path diagnostics diff --git a/benchmarks/agent_query/responsibility_source_followup_review_panel_a.json b/benchmarks/agent_query/responsibility_source_followup_review_panel_a.json new file mode 100644 index 000000000..17f96d17a --- /dev/null +++ b/benchmarks/agent_query/responsibility_source_followup_review_panel_a.json @@ -0,0 +1,807 @@ +{ + "schema": "compass.responsibility-source-followup-review/1", + "scope": "Development agent workflow over frozen native query outputs, with one source window selected only from returned subject anchors. This scores evidence coverage, not a synthesized explanation or native-only capability.", + "protocolCommit": "62f60b29", + "questionCommit": "232608ee", + "protocolSha256": "c5d87cf360d18dc8406b4308ae0e767399332f1ed2c23fbe77813a84d7807e85", + "questionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "captureSha256": "34b67bc4046b387e51d6cbe098f0a232e930ba14bfc64e7789a92dbb3729eb8d", + "collectorSha256": "c718e82c0d726c36d920d5791fe705a93a715c5aec6cfb6dd56cf22c0fa7010e", + "reviewerScriptSha256": "c067cbb4f3936b0fe048d63d2018f9f1f7a75f0cb3d8764ad6f7b840b920ba88", + "artifactRoot": "responsibility-source-followup-01", + "metrics": { + "compass": { + "requests": 5, + "successfulReads": 5, + "totalFacts": 20, + "sufficientCombinedEvidenceFacts": 11, + "fullFrozenWitnessContainmentFacts": 10, + "sourceBytes": 35692, + "queryBytes": 39740, + "combinedBytes": 75432 + }, + "graphify": { + "requests": 5, + "successfulReads": 5, + "totalFacts": 20, + "sufficientCombinedEvidenceFacts": 13, + "fullFrozenWitnessContainmentFacts": 12, + "sourceBytes": 35692, + "queryBytes": 53306, + "combinedBytes": 88998 + } + }, + "reviews": [ + { + "repository": "chi", + "tool": "compass", + "sourceFile": "mux.go", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceSha256": "f5eb1cd7b656cc6b9aba3fc9960085e33c03bb100cf91babb273c23ca62f9f60", + "querySha256": "37657db8665dda901e0e3befbd557c0987c833bc18c6e6722ae004b7c4e27bbf", + "queryBytes": 7949, + "sourceBytes": 8000, + "startLine": 21, + "lastCompleteLine": 258, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 21 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "chi-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": false, + "reason": "Middleware slice construction and the inline Mux pool/tree assignments are visible through line 258. The full frozen witness extends to the return at 262, but that return is not required by this fact.", + "witnesses": [ + { + "startLine": 242, + "endLine": 262, + "fullyContained": false + } + ] + }, + { + "id": "chi-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The computed-handler guard and panic are fully visible.", + "witnesses": [ + { + "startLine": 100, + "endLine": 106, + "fullyContained": true + } + ] + }, + { + "id": "chi-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The Route construct/configure/mount sequence lies outside the window.", + "witnesses": [ + { + "startLine": 278, + "endLine": 287, + "fullyContained": false + } + ] + }, + { + "id": "chi-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The routeHTTP lookup and handler invocation lie outside the window.", + "witnesses": [ + { + "startLine": 476, + "endLine": 493, + "fullyContained": false + } + ] + } + ] + }, + { + "repository": "chi", + "tool": "graphify", + "sourceFile": "mux.go", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "sourceSha256": "f5eb1cd7b656cc6b9aba3fc9960085e33c03bb100cf91babb273c23ca62f9f60", + "querySha256": "1bf6c1ffd510f5a1183367edaba6fb6a7a66764cd9a658fdaa9cfa3eeff6b52f", + "queryBytes": 6571, + "sourceBytes": 8000, + "startLine": 21, + "lastCompleteLine": 258, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 21 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "chi-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": false, + "reason": "Middleware slice construction and the inline Mux pool/tree assignments are visible through line 258. The full frozen witness extends to the return at 262, but that return is not required by this fact.", + "witnesses": [ + { + "startLine": 242, + "endLine": 262, + "fullyContained": false + } + ] + }, + { + "id": "chi-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The computed-handler guard and panic are fully visible.", + "witnesses": [ + { + "startLine": 100, + "endLine": 106, + "fullyContained": true + } + ] + }, + { + "id": "chi-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The Route construct/configure/mount sequence lies outside the window.", + "witnesses": [ + { + "startLine": 278, + "endLine": 287, + "fullyContained": false + } + ] + }, + { + "id": "chi-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The routeHTTP lookup and handler invocation lie outside the window.", + "witnesses": [ + { + "startLine": 476, + "endLine": 493, + "fullyContained": false + } + ] + } + ] + }, + { + "repository": "click", + "tool": "compass", + "sourceFile": "src/click/_compat.py", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "sourceSha256": "4224d55c98fa278ba766260c4a781363ce4ad082ca7f0e0c981dbdf2e1bd6ba8", + "querySha256": "09d1dab6c20017246f8e36ffe47f45c995cdf3b8f9d2e4fa9717f843769cdaf3", + "queryBytes": 7868, + "sourceBytes": 3692, + "startLine": 455, + "lastCompleteLine": 590, + "partialLastLine": false, + "truncated": false, + "declarationLines": [ + 455 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "click-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The wrapper assignments and name property are fully visible.", + "witnesses": [ + { + "startLine": 455, + "endLine": 464, + "fullyContained": true + } + ] + }, + { + "id": "click-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The closed guard, wrapped close, replace and final closed assignment are fully visible.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "fullyContained": true + } + ] + }, + { + "id": "click-3", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The __getattr__ forwarding body is fully visible.", + "witnesses": [ + { + "startLine": 473, + "endLine": 474, + "fullyContained": true + } + ] + }, + { + "id": "click-4", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "Both __exit__ and close are fully visible. The delete argument is passed but not inspected; no rollback is inferred.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "fullyContained": true + }, + { + "startLine": 479, + "endLine": 485, + "fullyContained": true + } + ] + } + ] + }, + { + "repository": "click", + "tool": "graphify", + "sourceFile": "src/click/_compat.py", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "sourceSha256": "4224d55c98fa278ba766260c4a781363ce4ad082ca7f0e0c981dbdf2e1bd6ba8", + "querySha256": "f8e7ae3aae06a943c686f3cd5f94cc7c7ea1f7da243ef8aa1ff35b5984564da5", + "queryBytes": 6850, + "sourceBytes": 3692, + "startLine": 455, + "lastCompleteLine": 590, + "partialLastLine": false, + "truncated": false, + "declarationLines": [ + 455 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "click-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The wrapper assignments and name property are fully visible.", + "witnesses": [ + { + "startLine": 455, + "endLine": 464, + "fullyContained": true + } + ] + }, + { + "id": "click-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The closed guard, wrapped close, replace and final closed assignment are fully visible.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "fullyContained": true + } + ] + }, + { + "id": "click-3", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The __getattr__ forwarding body is fully visible.", + "witnesses": [ + { + "startLine": 473, + "endLine": 474, + "fullyContained": true + } + ] + }, + { + "id": "click-4", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "Both __exit__ and close are fully visible. The delete argument is passed but not inspected; no rollback is inferred.", + "witnesses": [ + { + "startLine": 466, + "endLine": 471, + "fullyContained": true + }, + { + "startLine": 479, + "endLine": 485, + "fullyContained": true + } + ] + } + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "sourceFile": "src/main/java/org/jsoup/safety/Cleaner.java", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "sourceSha256": "78894b0c859a7e9de0ee6f7fb6d1c84bfff8a23e22e00247c8dd65f3b22a20a7", + "querySha256": "51bcb6e5217ff0bb974a18fb07a6b636d7a2b67c2ed5641c5da5ad1acf6c5a94", + "queryBytes": 7938, + "sourceBytes": 8000, + "startLine": 43, + "lastCompleteLine": 205, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 43 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "jsoup-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The constructor validation and reference assignment are fully visible.", + "witnesses": [ + { + "startLine": 50, + "endLine": 53, + "fullyContained": true + } + ] + }, + { + "id": "jsoup-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The separate shell, safe-node copy and cloned output settings are fully visible.", + "witnesses": [ + { + "startLine": 62, + "endLine": 70, + "fullyContained": true + } + ] + }, + { + "id": "jsoup-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The safe-tag predicate and beginning of attribute processing are visible, but the window stops before the destination attribute write and rejection branch. Partial evidence is not full credit.", + "witnesses": [ + { + "startLine": 148, + "endLine": 158, + "fullyContained": true + }, + { + "startLine": 188, + "endLine": 209, + "fullyContained": false + } + ] + }, + { + "id": "jsoup-4", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The fragment parse, discarded-count check and error-list condition are fully visible.", + "witnesses": [ + { + "startLine": 124, + "endLine": 133, + "fullyContained": true + } + ] + } + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "sourceFile": "src/main/java/org/jsoup/safety/Cleaner.java", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "sourceSha256": "78894b0c859a7e9de0ee6f7fb6d1c84bfff8a23e22e00247c8dd65f3b22a20a7", + "querySha256": "21db49e7171f06bfa3b36559f63a622705673ca94869932c49389803f09829c2", + "queryBytes": 6667, + "sourceBytes": 8000, + "startLine": 43, + "lastCompleteLine": 205, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 43 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "jsoup-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The constructor validation and reference assignment are fully visible.", + "witnesses": [ + { + "startLine": 50, + "endLine": 53, + "fullyContained": true + } + ] + }, + { + "id": "jsoup-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The separate shell, safe-node copy and cloned output settings are fully visible.", + "witnesses": [ + { + "startLine": 62, + "endLine": 70, + "fullyContained": true + } + ] + }, + { + "id": "jsoup-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The safe-tag predicate and beginning of attribute processing are visible, but the window stops before the destination attribute write and rejection branch. Partial evidence is not full credit.", + "witnesses": [ + { + "startLine": 148, + "endLine": 158, + "fullyContained": true + }, + { + "startLine": 188, + "endLine": 209, + "fullyContained": false + } + ] + }, + { + "id": "jsoup-4", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The fragment parse, discarded-count check and error-list condition are fully visible.", + "witnesses": [ + { + "startLine": 124, + "endLine": 133, + "fullyContained": true + } + ] + } + ] + }, + { + "repository": "redux", + "tool": "compass", + "sourceFile": "src/createStore.ts", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceSha256": "99c188e06d6c029273e0f8184ea284b31093e5d76f4d0ccf648c1527272b37d7", + "querySha256": "d30483e834045767618774140ade72fac87144bd43938a2360ab005ba9681235", + "queryBytes": 7997, + "sourceBytes": 8000, + "startLine": 41, + "lastCompleteLine": 264, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 41, + 75, + 86 + ], + "declarationAmbiguityRetained": true, + "factReviews": [ + { + "id": "redux-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The enhancer validation and delegation sequence are fully visible.", + "witnesses": [ + { + "startLine": 121, + "endLine": 134, + "fullyContained": true + } + ] + }, + { + "id": "redux-2", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The reducer update and finally-reset sequence are visible only in the Graphify-selected window.", + "witnesses": [ + { + "startLine": 293, + "endLine": 302, + "fullyContained": false + } + ] + }, + { + "id": "redux-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "Copy-on-write listener mutation is visible in both windows; the captured-map iteration is visible only in the Graphify-selected window.", + "witnesses": [ + { + "startLine": 152, + "endLine": 159, + "fullyContained": true + }, + { + "startLine": 221, + "endLine": 243, + "fullyContained": true + }, + { + "startLine": 304, + "endLine": 307, + "fullyContained": false + } + ] + }, + { + "id": "redux-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The returned store and observable adapter lie outside both windows.", + "witnesses": [ + { + "startLine": 344, + "endLine": 394, + "fullyContained": false + } + ] + } + ] + }, + { + "repository": "redux", + "tool": "graphify", + "sourceFile": "src/createStore.ts", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "sourceSha256": "8899477008d515644d77990476a8191d2bb5cd271be011fdbc7aae20560e9418", + "querySha256": "ba6c21a04ee4f7d720ee74726dd4551ce982429d97ade961ce50db31ef9a58da", + "queryBytes": 6609, + "sourceBytes": 8000, + "startLine": 86, + "lastCompleteLine": 307, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 86 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "redux-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The enhancer validation and delegation sequence are fully visible.", + "witnesses": [ + { + "startLine": 121, + "endLine": 134, + "fullyContained": true + } + ] + }, + { + "id": "redux-2", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The reducer update and finally-reset sequence are visible only in the Graphify-selected window.", + "witnesses": [ + { + "startLine": 293, + "endLine": 302, + "fullyContained": true + } + ] + }, + { + "id": "redux-3", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "Copy-on-write listener mutation is visible in both windows; the captured-map iteration is visible only in the Graphify-selected window.", + "witnesses": [ + { + "startLine": 152, + "endLine": 159, + "fullyContained": true + }, + { + "startLine": 221, + "endLine": 243, + "fullyContained": true + }, + { + "startLine": 304, + "endLine": 307, + "fullyContained": true + } + ] + }, + { + "id": "redux-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The returned store and observable adapter lie outside both windows.", + "witnesses": [ + { + "startLine": 344, + "endLine": 394, + "fullyContained": false + } + ] + } + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "sourceFile": "src/lib.rs", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceSha256": "d986270e959ecf66d941cbf9f5e7f0f35a657ad9dbe599f12e0857544bb270f3", + "querySha256": "4e0658ae31900521385690c31083b617977c322a9af823faf5f17505ad63be49", + "queryBytes": 7988, + "sourceBytes": 8000, + "startLine": 538, + "lastCompleteLine": 744, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 538, + 566 + ], + "declarationAmbiguityRetained": true, + "factReviews": [ + { + "id": "walkdir-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The iterator stack, depth pruning and error return are fully visible.", + "witnesses": [ + { + "startLine": 699, + "endLine": 724, + "fullyContained": true + } + ] + }, + { + "id": "walkdir-2", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The max_open close-before-open implementation lies outside the window.", + "witnesses": [ + { + "startLine": 901, + "endLine": 911, + "fullyContained": false + } + ] + }, + { + "id": "walkdir-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The symlink directory guard and ancestor-handle loop check lie outside the window.", + "witnesses": [ + { + "startLine": 961, + "endLine": 989, + "fullyContained": false + } + ] + }, + { + "id": "walkdir-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The deferred-directory implementation and depth filtering lie outside the window.", + "witnesses": [ + { + "startLine": 873, + "endLine": 898, + "fullyContained": false + } + ] + } + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "sourceFile": "src/lib.rs", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "sourceSha256": "570c63a0958c223231fa862b28bb71d3d1ea276e68f1cd18c7cd8289a441789c", + "querySha256": "729cd0be188021d0e815f4167dd547728feb812a8a497f638f9164f86f3028be", + "queryBytes": 26609, + "sourceBytes": 8000, + "startLine": 566, + "lastCompleteLine": 772, + "partialLastLine": true, + "truncated": true, + "declarationLines": [ + 566 + ], + "declarationAmbiguityRetained": false, + "factReviews": [ + { + "id": "walkdir-1", + "sufficientCombinedEvidence": true, + "fullFrozenWitnessContainment": true, + "reason": "The iterator stack, depth pruning and error return are fully visible.", + "witnesses": [ + { + "startLine": 699, + "endLine": 724, + "fullyContained": true + } + ] + }, + { + "id": "walkdir-2", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The max_open close-before-open implementation lies outside the window.", + "witnesses": [ + { + "startLine": 901, + "endLine": 911, + "fullyContained": false + } + ] + }, + { + "id": "walkdir-3", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The symlink directory guard and ancestor-handle loop check lie outside the window.", + "witnesses": [ + { + "startLine": 961, + "endLine": 989, + "fullyContained": false + } + ] + }, + { + "id": "walkdir-4", + "sufficientCombinedEvidence": false, + "fullFrozenWitnessContainment": false, + "reason": "The deferred-directory implementation and depth filtering lie outside the window.", + "witnesses": [ + { + "startLine": 873, + "endLine": 898, + "fullyContained": false + } + ] + } + ] + } + ], + "interpretation": [ + "All 40 tool/fact evidence judgments were reviewed. Manual semantic sufficiency credits Chi fact 1 from complete assignments before the partial line, while literal full-witness containment does not. Both measures are reported separately.", + "Graphify covers two additional Redux facts because its sole returned createStore anchor begins at implementation line 86. Compass retains overload lines 41 and 75 as well as implementation line 86, so the frozen earliest-anchor policy starts at 41. This is an observed workflow advantage under this policy, not a claim that dropping overload declarations is universally correct.", + "Compass WalkDir retains the associated type alias at line 538 and struct at 566. Neither declaration ambiguity was silently resolved. The policy selects a source window, not a winning declaration.", + "Each side returns 35692 source bytes. Initial query bytes differ, including Graphify WalkDir over-budget output, so combined payloads are 75432 and 88998 bytes. Actual disk reads also include bounded whole source files for validation and hashing; source payload bytes are not disk IO.", + "No synthesized prose answer or agent model was run. Source windows establish evidence available to an agent, not that either native query explained the facts.", + "One contiguous read is not a best-possible agent policy. Further source selection, disambiguation and native explain/get_node workflows remain unmeasured. This reused-repository panel is not held-out; graph precision, god-object diagnosis, functional community quality, latency and overall superiority remain unproven." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 7bbc1b975..6fb31047a 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1868,6 +1868,53 @@ preserve exact source witnesses, raw-response hashes, all omissions, the budget discrepancy, and the diagnosed edge. God-object responsibility judgments, functional community quality and overall superiority remain unproven. +### Bounded source follow-up for responsibility questions + +The [follow-up protocol](../../benchmarks/agent_query/responsibility_source_followup_panel_a.json) +was frozen in `62f60b29` before the source windows were read. Both sides get +one contiguous window of at most 8,000 bytes, beginning at the earliest exact +subject anchor returned by their initial query. A read requires one unique +file. All matching declaration anchors remain visible; choosing the window +origin does not resolve declaration identity. Oracle witness ranges do not +select the window. + +| Sufficient evidence after query plus source read | Compass | Graphify | +| --- | ---: | ---: | +| Chi | 2/4 | 2/4 | +| Click | 4/4 | 4/4 | +| jsoup | 3/4 | 3/4 | +| Redux | 1/4 | 3/4 | +| WalkDir | 1/4 | 1/4 | +| Total | 11/20 | 13/20 | +| Source payload bytes | 35,692 | 35,692 | +| Initial query plus source payload bytes | 75,432 | 88,998 | + +Graphify's two additional Redux facts concern reducer state updates and the +listener snapshot. Its returned anchor starts at implementation line 86; +Compass also returns overload declarations at lines 41 and 75, so its window +starts earlier and ends before those mechanisms. This is a workflow advantage +under the specified policy. It does not establish that omitting overloads is +universally correct. Compass likewise retains WalkDir's associated type alias +at line 538 and struct at line 566; neither ambiguity is silently resolved. + +The [review](../../benchmarks/agent_query/responsibility_source_followup_review_panel_a.json) +records all 40 evidence judgments and source/response hashes. Strict containment +of every complete frozen witness yields 10/20 and 12/20. Semantic review credits +one additional Chi fact per tool: the complete middleware construction and +shared pool/tree assignments appear before the partial last line, although the +window omits the later return in the witness. jsoup attribute filtering remains +incomplete because the window ends before the destination write and rejection +branch. These distinctions are preserved rather than treating partial ranges +uniformly as successes or failures. + +All ten reads succeeded. Click's window reaches EOF at 3,692 bytes; every other +window returns 8,000 bytes. The collector reads bounded whole files to validate +and hash them before slicing; payload bytes do not measure disk IO. No model +generated an explanation. This development arm measures evidence available to +an agent under one reading policy, with unequal initial query response sizes. +It does not measure best-possible workflows, source-reading latency, native +explanation quality, community quality, or god-object detection. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 090140f51aa583778078da692017f18667ae3893 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:51:10 -0700 Subject: [PATCH 43/97] fix: preserve literal identifier anchors in natural discovery --- CHANGELOG.md | 4 + crates/compass-cli/tests/code_query_cli.rs | 54 ++++++ crates/compass-query/src/discovery.rs | 202 ++++++++++++++++++++- docs/implementation/query-engine.md | 10 + 4 files changed, 267 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0a3e659..67070872d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Preserve literal compound identifiers in natural discovery questions ahead + of generic behavior matches, while retaining declaration collisions and + uncertainty from bounded name lookups. + - Preserve Java varargs signatures, parameter array types, and explicit array argument dimensions. Resolve supported overloads in strict, loose, then variable-arity order, retaining ambiguity when evidence cannot select a diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 8a26a7126..fc3d63084 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -632,6 +632,60 @@ fn discovery_cursor_survives_budget_alias_and_scope_order_but_rejects_graph_chan Ok(()) } +#[test] +fn natural_discovery_preserves_literal_subjects_in_prose() -> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph = support::write_typed_graph(directory.path())?; + let mut document = GraphDocument::load(&graph)?; + let subject = document + .nodes + .iter_mut() + .find(|node| node.id == "n:target") + .ok_or("missing target fixture")?; + subject.name = "_BufferedSink".to_owned(); + subject.qualified_name = "Fixture._BufferedSink".to_owned(); + let mut duplicate = subject.clone(); + duplicate.id = "n:duplicate".to_owned(); + duplicate.qualified_name = "Other._BufferedSink".to_owned(); + for ambiguous in [false, true] { + if ambiguous { + document.nodes.push(duplicate.clone()); + } + std::fs::write(&graph, serde_json::to_vec_pretty(&document)?)?; + let output = std::process::Command::new(env!("CARGO_BIN_EXE_compass")) + .args([ + "query", + "Explain _BufferedSink ownership and close behavior", + "--format=json", + "--graph", + ]) + .arg(&graph) + .output()?; + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.stderr.is_empty()); + let response: compass_model::query_contract::DiscoveryQueryResponse = + serde_json::from_slice(&output.stdout)?; + let seed = response.seeds.first().ok_or("missing subject seed")?; + assert_eq!( + seed.candidate_source, + compass_model::query_contract::DiscoverySeedSource::ExactName + ); + assert_eq!(seed.ambiguous, ambiguous); + assert!(["n:target", "n:duplicate"].contains(&seed.node_id.as_str())); + if ambiguous { + assert!(seed.alternatives.iter().any(|other| { + ["n:target", "n:duplicate"].contains(&other.node_id.as_str()) + && other.node_id != seed.node_id + })); + } + } + Ok(()) +} + #[test] fn natural_discovery_exposes_the_public_json_contract_and_repeatable_or_scopes() -> Result<(), Box> { diff --git a/crates/compass-query/src/discovery.rs b/crates/compass-query/src/discovery.rs index 4cbda3aa1..c9d237214 100644 --- a/crates/compass-query/src/discovery.rs +++ b/crates/compass-query/src/discovery.rs @@ -463,6 +463,37 @@ impl CodeQueryEngine { } } + // Preserve identifier spellings embedded in prose before generic + // behavior postings can fill the recall pool. The name index also + // contains aliases, so only a declared name equal to the literal earns + // exact-name rank. Every lookup and pool omission participates in the + // completeness proof; a bounded collision set is never unique. + for literal in literal_identifier_terms(question) { + guard.check()?; + if probes >= candidate_probe_limit || nodes_read >= candidate_read_limit { + exact_name_recall_complete = false; + truncated = true; + break; + } + probes += 1; + let remaining = candidate_read_limit.saturating_sub(nodes_read).min(64); + let (nodes, read_truncated) = + backend.nodes_by_normalized_name(&literal, remaining.max(1))?; + nodes_read = nodes_read.saturating_add(nodes.len()); + exact_name_recall_complete &= !read_truncated; + truncated |= read_truncated; + for node in nodes { + if discovery_scope_matches(&node, scope) + && crate::ranking::normalize_symbol_name(&node.name) == literal + { + let id = node.id.clone(); + let _ = pool.add(CandidateSource::ExactName, node); + let _ = pool.add_indexed_matches(&id, [literal.clone()]); + } + } + } + exact_name_recall_complete &= !pool.is_truncated(); + // An absent composite identifier is intentionally exact-only: the // final specificity gate below rejects every alias, term, // relationship, and fuzzy candidate for this query shape. Finish a @@ -518,7 +549,8 @@ impl CodeQueryEngine { role_pool.into_vec(), candidate_limit, ); - if !read.truncated + if exact_name_recall_complete + && !read.truncated && !role_pool_truncated && ranked.first().is_some_and(|candidate| { candidate @@ -586,7 +618,8 @@ impl CodeQueryEngine { let required_seed_count = usize::try_from(limits.max_seeds) .unwrap_or(usize::MAX) .min(usize::try_from(limits.max_nodes).unwrap_or(usize::MAX)); - if !read.truncated + if exact_name_recall_complete + && !read.truncated && !declaration_pool_truncated && ranked.len() >= required_seed_count && ranked @@ -1125,7 +1158,8 @@ impl CodeQueryEngine { || (candidate.channel_rank == 5 && exact_name_recall_complete) }); let operation_dominance_complete = ranked.first().is_some_and(|candidate| { - candidate.operation_root.is_some() + candidate.channel_rank < 5 + && candidate.operation_root.is_some() && operation_predicate_posting_complete( &candidate.node, &prepared.ranking_terms, @@ -1512,6 +1546,26 @@ fn is_explicit_binary_path_question( >= 2 } +fn literal_identifier_terms(question: &str) -> BTreeSet { + // Standalone names already use the full-query exact lookup. In prose, + // underscores and mixed-case internal capitals distinguish code spellings + // from ordinary words such as "Explain", "close", or "HTTP". This is a + // retrieval hint, not a claim that a named declaration exists or is unique. + if !question.chars().any(char::is_whitespace) { + return BTreeSet::new(); + } + question + .split(|character: char| !character.is_alphanumeric() && character != '_') + .filter(|token| { + token.chars().any(char::is_alphanumeric) + && (token.contains('_') + || (token.chars().any(char::is_lowercase) + && token.chars().skip(1).any(char::is_uppercase))) + }) + .map(crate::ranking::normalize_symbol_name) + .collect() +} + fn is_composite_identifier_query(question: &str, terms: &[String]) -> bool { !question.chars().any(char::is_whitespace) && question @@ -3016,6 +3070,148 @@ mod tests { Ok(()) } + #[test] + fn literal_identifiers_in_prose_keep_exact_name_priority() + -> Result<(), Box> { + for name in [ + "_BufferedSink", + "openSession", + "PendingQueue", + "flush_buffer", + ] { + let engine = engine( + vec![ + anchored_node("n:subject", name, "src/subject.rs", 1), + anchored_node("n:close", "close", "src/other.rs", 2), + anchored_node("n:context", "context", "src/context.rs", 3), + ], + vec![edge("e:context", "n:close", "n:context")], + ); + let mut query = request(DiscoveryDirection::Both); + query.question = + format!("Explain {name} ownership, close behavior and context cleanup."); + let response = engine.discover(query)?; + assert_eq!(response.seeds[0].node_id, "n:subject", "{name}"); + assert_eq!( + response.seeds[0].candidate_source, + DiscoverySeedSource::ExactName + ); + } + Ok(()) + } + + #[test] + fn literal_identifiers_in_prose_preserve_collisions() -> Result<(), Box> + { + let engine = engine( + vec![ + anchored_node("n:a", "openSession", "src/a.rs", 1), + anchored_node("n:b", "openSession", "src/b.rs", 2), + ], + Vec::new(), + ); + let mut query = request(DiscoveryDirection::Both); + query.question = "Explain openSession cleanup".to_owned(); + let response = engine.discover(query)?; + assert!(response.seeds[0].ambiguous); + assert_eq!( + response.seeds[0].candidate_source, + DiscoverySeedSource::ExactName + ); + assert!( + response.seeds[0] + .alternatives + .iter() + .any(|other| other.node_id != response.seeds[0].node_id) + ); + Ok(()) + } + + #[test] + fn literal_identifiers_in_prose_keep_multiple_subjects_and_report_pool_limits() + -> Result<(), Box> { + let engine = engine( + vec![ + anchored_node("n:a", "openSession", "src/a.rs", 1), + anchored_node("n:b", "closeSession", "src/b.rs", 2), + ], + Vec::new(), + ); + let mut query = request(DiscoveryDirection::Both); + query.question = "Compare openSession and closeSession behavior".to_owned(); + let response = engine.discover(query.clone())?; + assert_eq!(response.seeds.len(), 2); + assert!( + response + .seeds + .iter() + .all(|seed| seed.candidate_source == DiscoverySeedSource::ExactName) + ); + query.limits.max_candidates = 1; + let limited = engine.discover(query)?; + assert!(limited.truncated); + assert!(limited.seeds[0].ambiguous); + Ok(()) + } + + #[test] + fn literal_identifiers_in_prose_report_incomplete_name_postings() + -> Result<(), Box> { + let nodes = (0..70) + .map(|index| { + anchored_node( + &format!("n:{index:03}"), + "openSession", + &format!("src/{index}.rs"), + 1, + ) + }) + .collect(); + let engine = engine(nodes, Vec::new()); + let mut query = request(DiscoveryDirection::Both); + query.question = "Explain openSession cleanup".to_owned(); + // Only the first declaration is in scope. Truncated global postings + // still cannot prove that it is the only matching declaration here. + query.scope = vec![DiscoveryScope { + kind: DiscoveryScopeKind::Source, + value: "src/0.rs".to_owned(), + }]; + let response = engine.discover(query)?; + assert!(response.truncated); + assert_eq!(response.seeds.len(), 1); + assert_eq!(response.seeds[0].node_id, "n:000"); + assert!(response.seeds[0].ambiguous); + Ok(()) + } + + #[test] + fn literal_identifiers_in_prose_do_not_promote_plain_words_or_partial_names() + -> Result<(), Box> { + let engine = engine( + vec![ + anchored_node("n:close", "close", "src/close.rs", 1), + anchored_node("n:explain", "Explain", "src/explain.rs", 2), + anchored_node("n:partial", "Session", "src/session.rs", 3), + ], + Vec::new(), + ); + for question in [ + "Explain close behavior", + "Explain missingSession close behavior", + ] { + let mut query = request(DiscoveryDirection::Both); + query.question = question.to_owned(); + let response = engine.discover(query)?; + assert!( + response + .seeds + .iter() + .all(|seed| seed.candidate_source != DiscoverySeedSource::ExactName) + ); + } + Ok(()) + } + #[test] fn exact_name_beats_a_maximal_relationship_match() -> Result<(), Box> { let engine = engine( diff --git a/docs/implementation/query-engine.md b/docs/implementation/query-engine.md index 37486f691..a43bc1efb 100644 --- a/docs/implementation/query-engine.md +++ b/docs/implementation/query-engine.md @@ -246,6 +246,16 @@ semantic inference; a conceptual question can remain under-specified and must not be treated as an authoritative answer without an independently reviewed golden judgment. +Discovery also preserves code-shaped identifiers inside prose. Underscore +spellings and mixed-case internal capitals (such as `_BufferedSink`, +`openSession`, or `PendingQueue`) receive bounded exact-name probes before +generic behavior recall. Only a matching declared name receives exact-name +priority; a partial token or alias does not. Ordinary words such as `close` +retain lexical ranking. Duplicate declarations remain candidates, and a +truncated name lookup cannot establish uniqueness even if only one retained +declaration is in scope. This improves anchor selection without reading source +bodies or synthesizing an explanation. + ### Scoring `score_nodes` retains the established `text-ranker/full-scan-v1` behavior: it From 6711e3a3136eccdc63ed0d3559f7e5a11b45db3c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sat, 26 Sep 2026 23:57:52 -0700 Subject: [PATCH 44/97] fix: preserve ambiguity across exact declaration ranking evidence --- crates/compass-cli/tests/code_query_cli.rs | 1 + crates/compass-query/src/discovery.rs | 48 +++++++++++++++++----- docs/implementation/query-engine.md | 3 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index fc3d63084..ad1dec707 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -647,6 +647,7 @@ fn natural_discovery_preserves_literal_subjects_in_prose() -> Result<(), Box Result<(), Box> { + let mut declaration = anchored_node("n:struct", "PendingQueue", "src/queue.rs", 1); + declaration.kind = NodeKind::Struct; + let engine = engine( + vec![ + declaration, + anchored_node("n:factory", "PendingQueue", "src/factory.rs", 2), + ], + Vec::new(), + ); + let mut query = request(DiscoveryDirection::Both); + query.question = "Explain PendingQueue ownership".to_owned(); + let response = engine.discover(query)?; + assert_eq!(response.seeds.len(), 2); + assert!(response.seeds.iter().all(|seed| seed.ambiguous)); + Ok(()) + } + #[test] fn literal_identifiers_in_prose_keep_multiple_subjects_and_report_pool_limits() -> Result<(), Box> { diff --git a/docs/implementation/query-engine.md b/docs/implementation/query-engine.md index a43bc1efb..6a9000260 100644 --- a/docs/implementation/query-engine.md +++ b/docs/implementation/query-engine.md @@ -251,7 +251,8 @@ spellings and mixed-case internal capitals (such as `_BufferedSink`, `openSession`, or `PendingQueue`) receive bounded exact-name probes before generic behavior recall. Only a matching declared name receives exact-name priority; a partial token or alias does not. Ordinary words such as `close` -retain lexical ranking. Duplicate declarations remain candidates, and a +retain lexical ranking. Same-name source declarations remain ambiguous even +when their kinds or ranking evidence differ, and a truncated name lookup cannot establish uniqueness even if only one retained declaration is in scope. This improves anchor selection without reading source bodies or synthesizing an explanation. From b8b1cfd283c209e690aed5e55623eab9f3233f82 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:05:29 -0700 Subject: [PATCH 45/97] test: record literal identifier query diagnostics and verification --- COMPATIBILITY.md | 15 +- benchmarks/agent_query/COVERAGE_PLAN.md | 7 + ...literal_identifier_development_review.json | 300 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 44 +++ 4 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 benchmarks/agent_query/literal_identifier_development_review.json diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index cd808c7b2..f46e0cb55 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -923,11 +923,22 @@ persistence, dispatch, invocation, processing, recognition, refresh, resolution, and scheduling) affect ranking only: they cannot add a posting, candidate, relationship concept, or relation eligibility. Equal evidence vectors remain explicitly ambiguous. -Natural-query alternatives now require the same channel, operation, +Lexical natural-query alternatives require the same channel, operation, relationship, and calibrated score rank before they are labeled ambiguous. This removes false ambiguity between a specifically ranked operation or representation and a weaker same-name/helper candidate. Equal-rank candidates -and duplicate exact-name lookups remain explicit ambiguity. +and duplicate exact-name lookups remain explicit ambiguity. Exact matches to +the same source-backed declaration name remain ambiguous across differences +in kind, signature, owner, and ranking evidence; ranking does not prove which +declaration the user meant. + +Within prose, underscore spellings and mixed-case internal capitals receive +bounded literal-name lookup before behavior recall. Only declared-name matches +receive exact-name priority. This changes candidate ordering without changing +the discovery schema or graph format. Truncated name postings or candidate +admission cannot prove uniqueness, and generic operation ranking cannot +override that uncertainty. Single-word capitalized names continue through the +existing ranking unless the whole question is an exact name. For explicit action predicates, discovery first reads one compact exact-term index restricted to source-backed operation-role declarations. It may finish diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 2bad05460..eee872853 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -133,6 +133,13 @@ also returns preceding overload declarations. Keep that policy effect and all declaration ambiguities visible. This measures available source evidence, not native explanation quality; further reading and disambiguation remain open. +`literal_identifier_development_review.json` records the resulting retrieval +fix and its fixed-graph reruns. Literal compound identifiers gain exact-name +priority; duplicate declarations remain ambiguous across ranking evidence. +The unchanged suite remains 49/55 versus 46/55 on its recall proxy. Keep the +anchor diagnostics separate from explanation completeness and retain the +single-word subject failures. + ### MCP path diagnostics `suite_mcp_paths.json` covers prepared exact-ID navigation, reverse traversal, diff --git a/benchmarks/agent_query/literal_identifier_development_review.json b/benchmarks/agent_query/literal_identifier_development_review.json new file mode 100644 index 000000000..09686de7c --- /dev/null +++ b/benchmarks/agent_query/literal_identifier_development_review.json @@ -0,0 +1,300 @@ +{ + "schema": "compass.literal-identifier-development-review/1", + "sourceCommits": [ + "090140f5", + "6711e3a3" + ], + "scope": "Post-diagnostic query improvement on reused five-language repositories and fixed graph files. No extraction, held-out confirmation, latency comparison or overall superiority claim.", + "artifactRoot": "literal-identifiers-02", + "captureSha256": "49ad76b89b5a20c4c769fc5b5723a0e6a5184c706ba336f77d9d6cfee1c99842", + "collectorSha256": "1b48fe2ef13381878f2189ee0925a614a4712397edbc76bdb5e441f73ba5af86", + "reviewerSha256": "d39ba8337e5617d69c61b8975cd7cbd212d42af87ad879fb1d04ec37718ec1fe", + "rawManifestSha256": "3c6379cd6b6704333fed267b096222c926e8b5943ba2e6a0214ba425ddbbb7a1", + "rawFiles": 248, + "sourceFileSha256": { + "crates/compass-query/src/discovery.rs": "768dee69664d6af73e2c90adb6aae10d8f678d3731bfc68a680c1a565aabcd0f", + "crates/compass-cli/tests/code_query_cli.rs": "07d0289c9e1f74d28eb99bba16413052144e222d6a76a35e464937fd0aa0ec42" + }, + "tools": { + "compass": { + "sha256": "0bd42c8b4b5e88622747ec80920daa36a522ff326355fb35adea3b55f5e9fe3f", + "digestScope": "executable-file-only" + }, + "graphify": { + "sha256": "a7fdb4ac8985755be15f10f89a2d17ee517130b3ac90c28c91644ae351881da3", + "digestScope": "executable-file-only" + } + }, + "sourceRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "suiteSha256": "820a5c29f59e68b4ee8493e153c806b23e458a396431f3cf381660e469391237", + "runnerSha256": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "comparativeRecall": { + "compass": { + "requests": 55, + "passed": 49 + }, + "graphify": { + "requests": 55, + "passed": 46 + } + }, + "verdictChanges": [], + "responsibilityResponseDiagnostics": [ + { + "repository": "chi", + "tool": "compass", + "beforeSha256": "37657db8665dda901e0e3befbd557c0987c833bc18c6e6722ae004b7c4e27bbf", + "afterSha256": "37657db8665dda901e0e3befbd557c0987c833bc18c6e6722ae004b7c4e27bbf", + "stdoutBytes": 7949, + "beforeSeedRows": [ + "SEED chi.Mux::Route [source=alias; matched=mux,route]", + "SEED middleware.HeaderRouter::Route [source=alias; matched=middleware,route,router]", + "SEED chi.Mux::Routes [source=term_index; matched=mux]" + ], + "afterSeedRows": [ + "SEED chi.Mux::Route [source=alias; matched=mux,route]", + "SEED middleware.HeaderRouter::Route [source=alias; matched=middleware,route,router]", + "SEED chi.Mux::Routes [source=term_index; matched=mux]" + ], + "ambiguousSeedWarnings": 1, + "lineMultisetUnchanged": true + }, + { + "repository": "chi", + "tool": "graphify", + "beforeSha256": "1bf6c1ffd510f5a1183367edaba6fb6a7a66764cd9a658fdaa9cfa3eeff6b52f", + "afterSha256": "1bf6c1ffd510f5a1183367edaba6fb6a7a66764cd9a658fdaa9cfa3eeff6b52f", + "stdoutBytes": 6571, + "beforeSeedRows": [], + "afterSeedRows": [], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": true + }, + { + "repository": "click", + "tool": "compass", + "beforeSha256": "09d1dab6c20017246f8e36ffe47f45c995cdf3b8f9d2e4fa9717f843769cdaf3", + "afterSha256": "d96d1f8f106be2093d3613fe9656b885f238ff9c2819d476f151ff8e0562bec0", + "stdoutBytes": 7966, + "beforeSeedRows": [ + "SEED src.click.core.Context::close [source=alias; matched=close,context,exception]", + "SEED src.click.utils._LazyFile::close [source=alias; matched=close,file]", + "SEED src.click._compat._AtomicFile::close [source=alias; matched=_atomic,close,file]" + ], + "afterSeedRows": [ + "SEED src.click._compat._AtomicFile [source=exact_name; matched=file]", + "SEED src.click.core.Context::close [source=alias; matched=close,context,exception]", + "SEED src.click.utils._LazyFile::close [source=alias; matched=close,file]" + ], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": false + }, + { + "repository": "click", + "tool": "graphify", + "beforeSha256": "f8e7ae3aae06a943c686f3cd5f94cc7c7ea1f7da243ef8aa1ff35b5984564da5", + "afterSha256": "f8e7ae3aae06a943c686f3cd5f94cc7c7ea1f7da243ef8aa1ff35b5984564da5", + "stdoutBytes": 6850, + "beforeSeedRows": [], + "afterSeedRows": [], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": true + }, + { + "repository": "jsoup", + "tool": "compass", + "beforeSha256": "51bcb6e5217ff0bb974a18fb07a6b636d7a2b67c2ed5641c5da5ad1acf6c5a94", + "afterSha256": "51bcb6e5217ff0bb974a18fb07a6b636d7a2b67c2ed5641c5da5ad1acf6c5a94", + "stdoutBytes": 7938, + "beforeSeedRows": [ + "SEED org.jsoup.safety.Cleaner::clean [source=alias; matched=clean,cleaner]", + "SEED org.jsoup.safety.Cleaner [source=alias; matched=cleaner]", + "SEED org.jsoup.safety.Safelist [source=alias; matched=safelist]" + ], + "afterSeedRows": [ + "SEED org.jsoup.safety.Cleaner::clean [source=alias; matched=clean,cleaner]", + "SEED org.jsoup.safety.Cleaner [source=alias; matched=cleaner]", + "SEED org.jsoup.safety.Safelist [source=alias; matched=safelist]" + ], + "ambiguousSeedWarnings": 1, + "lineMultisetUnchanged": true + }, + { + "repository": "jsoup", + "tool": "graphify", + "beforeSha256": "21db49e7171f06bfa3b36559f63a622705673ca94869932c49389803f09829c2", + "afterSha256": "21db49e7171f06bfa3b36559f63a622705673ca94869932c49389803f09829c2", + "stdoutBytes": 6667, + "beforeSeedRows": [], + "afterSeedRows": [], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": true + }, + { + "repository": "redux", + "tool": "compass", + "beforeSha256": "d30483e834045767618774140ade72fac87144bd43938a2360ab005ba9681235", + "afterSha256": "61c7e11bb1a2688b1dd9d03f470578e3c8e9def2855312520cf32fe19bd400cf", + "stdoutBytes": 7939, + "beforeSeedRows": [ + "SEED store.Dispatch [source=alias; matched=dispatch,store]", + "SEED store.Store [source=term_index; matched=store]", + "SEED createStore.createStore [source=term_index; matched=create,store]" + ], + "afterSeedRows": [ + "SEED createStore.createStore [source=exact_name; matched=create,store]", + "SEED createStore.createStore [source=exact_name; matched=create,store]", + "SEED createStore.createStore [source=exact_name; matched=create,store]" + ], + "ambiguousSeedWarnings": 3, + "lineMultisetUnchanged": false + }, + { + "repository": "redux", + "tool": "graphify", + "beforeSha256": "ba6c21a04ee4f7d720ee74726dd4551ce982429d97ade961ce50db31ef9a58da", + "afterSha256": "ba6c21a04ee4f7d720ee74726dd4551ce982429d97ade961ce50db31ef9a58da", + "stdoutBytes": 6609, + "beforeSeedRows": [], + "afterSeedRows": [], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": true + }, + { + "repository": "walkdir", + "tool": "compass", + "beforeSha256": "4e0658ae31900521385690c31083b617977c322a9af823faf5f17505ad63be49", + "afterSha256": "3e5d0641ad3345954446273004e4df1dcc15638ca7743af5dfea4eeaf007df23", + "stdoutBytes": 7988, + "beforeSeedRows": [ + "SEED ::IntoIter [source=term_index; matched=iter]", + "SEED walkdir::IntoIter [source=term_index; matched=iter]", + "SEED walkdir::IntoIter::oldest_opened [source=term_index; matched=iter]" + ], + "afterSeedRows": [ + "SEED ::IntoIter [source=exact_name; matched=iter]", + "SEED walkdir::IntoIter [source=exact_name; matched=iter]", + "SEED walkdir::IntoIter::oldest_opened [source=term_index; matched=iter]" + ], + "ambiguousSeedWarnings": 2, + "lineMultisetUnchanged": false + }, + { + "repository": "walkdir", + "tool": "graphify", + "beforeSha256": "729cd0be188021d0e815f4167dd547728feb812a8a497f638f9164f86f3028be", + "afterSha256": "cc7707051db63230b38ff79ded253197a5885ce0576b72818b8c67809d453989", + "stdoutBytes": 26609, + "beforeSeedRows": [], + "afterSeedRows": [], + "ambiguousSeedWarnings": 0, + "lineMultisetUnchanged": true + } + ], + "verification": { + "fmt": { + "file": "literal-identifiers-final-fmt.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "passed": 0, + "failed": 0, + "ignored": 0, + "testBinaries": 0 + }, + "cli": { + "file": "literal-identifiers-final-cli.log", + "sha256": "c12ba8c90191e4deb956e8ec1669f89acc7e055678f124d3e9e7c8d586d44e57", + "passed": 37, + "failed": 0, + "ignored": 0, + "testBinaries": 1 + }, + "clippy": { + "file": "literal-identifiers-final-clippy.log", + "sha256": "6d31f6b1b212dc557bb58b86087e5a0e0ecd982be4dd1e67469c91a85316cc45", + "passed": 0, + "failed": 0, + "ignored": 0, + "testBinaries": 0 + }, + "workspace": { + "file": "literal-identifiers-final-workspace.log", + "sha256": "0ae8e0c9bcba9ce735e0470f205d9a1396b16d78550ea0dc75df12c592511961", + "passed": 1098, + "failed": 0, + "ignored": 2, + "testBinaries": 36 + }, + "query-integration": { + "file": "literal-identifiers-final-query-integration.log", + "sha256": "5f849a6ea3007a512faa898abe8005203a926792da86c129ab767e85c987798f", + "passed": 310, + "failed": 0, + "ignored": 0, + "testBinaries": 22 + }, + "product": { + "file": "literal-identifiers-final-product.log", + "sha256": "865ab9abec073a33b12deb5eb77ad86ce27306d2de4e96d06b9473e2e8c33def", + "passed": 9, + "failed": 0, + "ignored": 0, + "testBinaries": 1 + }, + "boundary": { + "file": "literal-identifiers-final-boundary.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "passed": 0, + "failed": 0, + "ignored": 0, + "testBinaries": 0 + } + }, + "developmentHistory": [ + { + "stage": "Initial literal-name regressions", + "log": "literal-identifiers-before.log", + "sha256": "ea746a95afb3c71742eee1261c57c9890c46d33bc1dbfb1e20db9d5a85408037", + "result": "One passed; exact provenance assertions failed in two tests before implementation." + }, + { + "stage": "Bounded lookup completeness regression", + "log": "literal-identifiers-query-tests-02.log", + "sha256": "c87d46c77c246bafc5dffd94a1b98fe256e062fa7a1e93cc60efc8b4f7276ef5", + "result": "184 passed, one failed: operation ranking incorrectly overrode truncated exact-name evidence. Fixed before the first real-repository candidate run." + }, + { + "stage": "Initial candidate comparison", + "artifactRoot": "literal-identifiers-01", + "captureSha256": "99252edb505910b27fbc51f111327121bff1db564fe746b28df6f73610f26888", + "result": "110 suite requests plus ten responsibility queries completed; unchanged 49/55 versus 46/55 recall. Redux exposed inconsistent per-seed ambiguity; Click displayed matched=none." + }, + { + "stage": "Initial workspace verification interrupted", + "log": "literal-identifiers-clippy.log", + "sha256": "162c4ffa5e5799e2ef69d0a11b01c7e803e2f12b6646b64477bac61b89a464c1", + "result": "Validation process group intentionally terminated with exit 143 to fix the observed collision classification. Not a completed baseline; superseded by final verification." + }, + { + "stage": "Cross-kind exact collision regression", + "log": "literal-identifiers-metadata-before.log", + "sha256": "8228ddc03ea4885c129d8cb90287ffcc6f9cc20a1279004c5321d922f5b29f5f", + "result": "Five passed; same-name struct/function ambiguity test failed before correction." + }, + { + "stage": "Final query library verification", + "log": "literal-identifiers-query-tests-04.log", + "sha256": "49a1f70f3113a34e2ced9a10c46bbe3851c8f2d055ff985d170daca0bc00965a", + "result": "186 passed, zero failed." + } + ], + "interpretation": [ + "Literal underscores and internal mixed-case capitals now receive bounded declared-name probes ahead of generic behavior terms. Missing literal spellings do not promote partial names, and ordinary words retain lexical ranking.", + "Exact-name collisions remain ambiguous across ranking evidence and declaration kinds. Global posting truncation and pool truncation cannot establish uniqueness. Six focused unit regressions plus an actual CLI subprocess regression exercise these boundaries.", + "Click _AtomicFile moves from surrounding-node context to the first exact-name seed. Redux createStore declarations all become exact-name seeds and all three carry ambiguity warnings. This is a post-output anchor diagnostic, not a new scored success rate.", + "Chi Mux and jsoup Cleaner outputs remain byte-identical. Single capitalized words do not receive the new literal-compound treatment. Further subject understanding remains open.", + "The unchanged 55-question suite per tool retains 49 versus 46 recall-proxy passes with no verdict changes. These checks do not establish full edge precision, source occurrence correctness or answer completeness.", + "Responsibility response rows and their actual byte sizes are retained. The earlier 11/20 versus 13/20 bounded source-follow-up result belongs to its frozen pre-change workflow; no new source-follow-up fact score is inferred here.", + "The query integration log includes the query library tests again; verification groups overlap and must not be summed as unique tests. The workspace log contains a pre-existing compass-core test unused_mut warning; the strict production Clippy invocation completes successfully.", + "Extraction, parser and graph publication code did not change in these commits. The prior extraction qualification is retained separately; no new full fixture/release-extraction gate is claimed for this query-only iteration. Fresh held-out evaluation, god-object diagnosis and functional community quality remain unfinished." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 6fb31047a..a81c5bf5f 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1915,6 +1915,50 @@ an agent under one reading policy, with unequal initial query response sizes. It does not measure best-possible workflows, source-reading latency, native explanation quality, community quality, or god-object detection. +### Literal identifiers in natural questions + +The responsibility responses exposed a retrieval gap: Click's question names +`_AtomicFile`, but generic `close` methods occupy all three seed slots. Commits +`090140f5` and `6711e3a3` add bounded declared-name lookup for underscore and +mixed-case compound identifiers embedded in prose. Ordinary words retain their +lexical rank; a missing compound does not turn partial names into exact hits. +All lookups share the existing work bounds, and incomplete name postings or +candidate admission cannot establish uniqueness. + +The first implementation exposed a second problem in the real Redux response: +the top `createStore` seed lacked an ambiguity flag while the other same-name +declarations had one. A regression with same-name declarations of different +kinds failed before correction. Exact source-name collisions now stay ambiguous +across ranking evidence; heuristic ranking orders them without resolving their +identity. Matched identifier components are retained even when later recall +channels cannot admit more candidates. + +The final fixed-graph rerun executes all 110 existing suite requests and the ten +responsibility queries against the same graph files and source pins. The +existing recall proxy remains **49/55 for Compass and 46/55 for Graphify**, with +no verdict changes or timeouts. The responsibility diagnostics show: + +- Click's first seed is now `_AtomicFile`, with exact-name provenance and a + matched identifier component. Generic secondary candidates and partial + coverage remain. +- All three Redux `createStore` declarations are exact-name seeds and all + three carry ambiguity warnings. +- WalkDir's associated alias and `IntoIter` struct are both exact-name seeds + with ambiguity warnings. +- Chi `Mux` and jsoup `Cleaner` outputs remain byte-identical. Single-word + capitalized subjects still use the earlier ranking. + +These are anchor-selection diagnostics on a development panel, not a new +explanation score. The earlier source-follow-up score belongs to its frozen +pre-change workflow. The +[development review](../../benchmarks/agent_query/literal_identifier_development_review.json) +records both candidate runs, failed-before regressions, final verification, +source and binary hashes, and actual response sizes. The first workspace +verification batch was intentionally stopped for the ambiguity correction and +is not counted as a completed baseline. Extraction was not rerun for these +query-only changes. Full response precision, new source-follow-up coverage, +held-out confirmation, god-object diagnosis and community quality remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 234753eb2439ab01c29624d3c8d1bd28336f6062 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:12:03 -0700 Subject: [PATCH 46/97] test: freeze source-defined community task pairs across five languages --- .../community_task_pairs_panel_a.json | 488 ++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 benchmarks/agent_query/community_task_pairs_panel_a.json diff --git a/benchmarks/agent_query/community_task_pairs_panel_a.json b/benchmarks/agent_query/community_task_pairs_panel_a.json new file mode 100644 index 000000000..4fd5c46cb --- /dev/null +++ b/benchmarks/agent_query/community_task_pairs_panel_a.json @@ -0,0 +1,488 @@ +{ + "schema": "compass.community-task-pairs/1", + "scope": "Source-defined task-local community diagnostic on reused development repositories. Frozen before inspecting assignments for these selected declarations. Prior unrelated graph outputs have been observed; this is not held-out or independent review.", + "sourceRun": "java-varargs-panel-a-02", + "sourceRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "policy": { + "unit": "Exact file, declaration start and terminal symbol; retain every matching node identity. Missing, ambiguous or unassigned declarations are unresolved, never a successful separation.", + "artifactComparison": "Inspect each tool native-only stored community assignments on its unchanged captured graph. This is an artifact-quality diagnostic, not a timed CLI/MCP workflow or synthesized answer.", + "pairSelection": "All unordered pairs among the six selected declarations per repository: three within-task collaborator pairs and twelve cross-task pairs. No output selects or drops a pair.", + "metrics": "Report same-community, different-community and unresolved counts separately for within-task and cross-task pairs, stratified by same-file versus cross-file. Report whole-community sizes and source-file diversity for context. Never combine these into an accuracy or superiority score.", + "interpretation": "Within-task co-location measures one way to retain a reviewed collaborator. Cross-task co-location describes grouping granularity, not an automatic defect: tasks can share helpers and state or belong to one coherent subsystem. Different community IDs have no cross-tool meaning.", + "bounds": { + "maxGraphBytes": 268435456, + "maxNodes": 200000, + "maxEdges": 1000000 + }, + "counterevidence": [ + "Click help-table measurement calls term_len, linking two named tasks directly; putting them together may be useful.", + "WalkDir tasks are cooperating parts of one iterator and share configuration or traversal state. Separating all of them is not a required architecture.", + "Chi router construction and pattern handling both support routing; a large core community can be coherent.", + "jsoup parsing, safe copying and whitespace classification can cooperate in HTML processing.", + "Redux action validation supports store behavior as well as standalone checks; the small pairs do not partition all responsibilities." + ], + "limitations": [ + "Only 30 purposively selected declarations and 75 pairs; two within-task pairs cross files. Results are not population precision or a whole-system architecture oracle.", + "Task annotations are source-backed reviewer judgments. They do not establish exclusive ownership, refactoring need, god-object positives/negatives, or independently confirmed semantic labels.", + "Graph edge/source correctness outside the exact declaration witnesses remains unaudited in this arm.", + "No cluster algorithm may be tuned merely to maximize these co-location counts; review actual source mechanisms and granularity tradeoffs first." + ] + }, + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "graphSha256": { + "compass": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphify": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + "tasks": [ + { + "id": "chi-router-construction", + "task": "Construct the router exposed by the public factory.", + "sourceReason": "NewRouter delegates directly to NewMux, which initializes its routing tree and context pool.", + "declarations": [ + { + "id": "chi-router-construction-NewRouter", + "symbol": "NewRouter", + "ownerContext": "package chi", + "file": "chi.go", + "startLine": 62, + "witnessEndLine": 64, + "sourceFileSha256": "47c70ececcbb9d71f973eda3cbadad0a46c8cc2261b285f7049b5261f337d678", + "witness": "func NewRouter() *Mux {\n\treturn NewMux()\n}" + }, + { + "id": "chi-router-construction-NewMux", + "symbol": "NewMux", + "ownerContext": "package chi", + "file": "mux.go", + "startLine": 52, + "witnessEndLine": 59, + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "witness": "func NewMux() *Mux {\n\tmux := &Mux{tree: &node{}, pool: &sync.Pool{}}\n\tmux.pool.New = func() any {\n\t\treturn NewRouteContext()\n\t}\n\treturn mux\n}\n" + } + ] + }, + { + "id": "chi-request-identity", + "task": "Attach and retrieve an HTTP request identifier.", + "sourceReason": "RequestID stores a value under RequestIDKey; GetReqID retrieves that same context value.", + "declarations": [ + { + "id": "chi-request-identity-RequestID", + "symbol": "RequestID", + "ownerContext": "package middleware", + "file": "middleware/request_id.go", + "startLine": 67, + "witnessEndLine": 80, + "sourceFileSha256": "31b21034dd5cd6393fa9abc9ce1207acc78fea1e162d866edf6bb2a7badd288b", + "witness": "func RequestID(next http.Handler) http.Handler {\n\tfn := func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := r.Context()\n\t\trequestID := r.Header.Get(RequestIDHeader)\n\t\tif requestID == \"\" {\n\t\t\tmyid := reqid.Add(1)\n\t\t\trequestID = fmt.Sprintf(\"%s-%06d\", prefix, myid)\n\t\t}\n\t\tctx = context.WithValue(ctx, RequestIDKey, requestID)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t}\n\treturn http.HandlerFunc(fn)\n}\n" + }, + { + "id": "chi-request-identity-GetReqID", + "symbol": "GetReqID", + "ownerContext": "package middleware", + "file": "middleware/request_id.go", + "startLine": 83, + "witnessEndLine": 91, + "sourceFileSha256": "31b21034dd5cd6393fa9abc9ce1207acc78fea1e162d866edf6bb2a7badd288b", + "witness": "func GetReqID(ctx context.Context) string {\n\tif ctx == nil {\n\t\treturn \"\"\n\t}\n\tif reqID, ok := ctx.Value(RequestIDKey).(string); ok {\n\t\treturn reqID\n\t}\n\treturn \"\"\n}" + } + ] + }, + { + "id": "chi-route-pattern-parameters", + "task": "Read parameter keys from a route pattern.", + "sourceReason": "patParamKeys repeatedly calls patNextSegment, checks duplicate keys and advances through the pattern.", + "declarations": [ + { + "id": "chi-route-pattern-parameters-patParamKeys", + "symbol": "patParamKeys", + "ownerContext": "package chi", + "file": "tree.go", + "startLine": 803, + "witnessEndLine": 819, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "witness": "func patParamKeys(pattern string) []string {\n\tpat := pattern\n\tparamKeys := []string{}\n\tfor {\n\t\tptyp, paramKey, _, _, _, e := patNextSegment(pat)\n\t\tif ptyp == ntStatic {\n\t\t\treturn paramKeys\n\t\t}\n\t\tfor i := 0; i < len(paramKeys); i++ {\n\t\t\tif paramKeys[i] == paramKey {\n\t\t\t\tpanic(fmt.Sprintf(\"chi: routing pattern '%s' contains duplicate param key, '%s'\", pattern, paramKey))\n\t\t\t}\n\t\t}\n\t\tparamKeys = append(paramKeys, paramKey)\n\t\tpat = pat[e:]\n\t}\n}" + }, + { + "id": "chi-route-pattern-parameters-patNextSegment", + "symbol": "patNextSegment", + "ownerContext": "package chi", + "file": "tree.go", + "startLine": 735, + "witnessEndLine": 801, + "sourceFileSha256": "f4b12b63b662fb8e36658172b36b35705cfb24eefae0665635f4fbd52e64fb79", + "witness": "func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) {\n\tps := strings.Index(pattern, \"{\")\n\tws := strings.Index(pattern, \"*\")\n\n\tif ps < 0 && ws < 0 {\n\t\treturn ntStatic, \"\", \"\", 0, 0, len(pattern) // we return the entire thing\n\t}\n\n\t// Sanity check\n\tif ps >= 0 && ws >= 0 && ws < ps {\n\t\tpanic(\"chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'\")\n\t}\n\n\tvar tail byte = '/' // Default endpoint tail to / byte\n\n\tif ps >= 0 {\n\t\t// Param/Regexp pattern is next\n\t\tnt := ntParam\n\n\t\t// Read to closing } taking into account opens and closes in curl count (cc)\n\t\tcc := 0\n\t\tpe := ps\n\t\tfor i, c := range pattern[ps:] {\n\t\t\tif c == '{' {\n\t\t\t\tcc++\n\t\t\t} else if c == '}' {\n\t\t\t\tcc--\n\t\t\t\tif cc == 0 {\n\t\t\t\t\tpe = ps + i\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif pe == ps {\n\t\t\tpanic(\"chi: route param closing delimiter '}' is missing\")\n\t\t}\n\n\t\tkey := pattern[ps+1 : pe]\n\t\tpe++ // set end to next position\n\n\t\tif pe < len(pattern) {\n\t\t\ttail = pattern[pe]\n\t\t}\n\n\t\tkey, rexpat, isRegexp := strings.Cut(key, \":\")\n\t\tif isRegexp {\n\t\t\tnt = ntRegexp\n\t\t}\n\n\t\tif len(rexpat) > 0 {\n\t\t\tif rexpat[0] != '^' {\n\t\t\t\trexpat = \"^\" + rexpat\n\t\t\t}\n\t\t\tif rexpat[len(rexpat)-1] != '$' {\n\t\t\t\trexpat += \"$\"\n\t\t\t}\n\t\t}\n\n\t\treturn nt, key, rexpat, tail, ps, pe\n\t}\n\n\t// Wildcard pattern as finale\n\tif ws < len(pattern)-1 {\n\t\tpanic(\"chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead\")\n\t}\n\treturn ntCatchAll, \"*\", \"\", 0, ws, len(pattern)\n}" + } + ] + } + ] + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "graphSha256": { + "compass": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphify": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + "tasks": [ + { + "id": "click-atomic-close", + "task": "Follow atomic-file replacement and context cleanup.", + "sourceReason": "The context exit delegates to close; close checks closed, closes the wrapped stream and replaces the destination. The delete parameter does not implement rollback.", + "declarations": [ + { + "id": "click-atomic-close-__exit__", + "symbol": "__exit__", + "ownerContext": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 479, + "witnessEndLine": 485, + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "witness": " def __exit__(\n self,\n exc_type: type[BaseException] | None,\n exc_value: BaseException | None,\n tb: TracebackType | None,\n ) -> None:\n self.close(delete=exc_type is not None)" + }, + { + "id": "click-atomic-close-close", + "symbol": "close", + "ownerContext": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 466, + "witnessEndLine": 471, + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "witness": " def close(self, delete: bool = False) -> None:\n if self.closed:\n return\n self._f.close()\n os.replace(self._tmp_filename, self._real_filename)\n self.closed = True" + } + ] + }, + { + "id": "click-terminal-string-width", + "task": "Measure visible terminal text without ANSI escapes.", + "sourceReason": "term_len takes the length after strip_ansi removes escape sequences.", + "declarations": [ + { + "id": "click-terminal-string-width-term_len", + "symbol": "term_len", + "ownerContext": "module", + "file": "src/click/_compat.py", + "startLine": 536, + "witnessEndLine": 537, + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "witness": "def term_len(x: str) -> int:\n return len(strip_ansi(x))" + }, + { + "id": "click-terminal-string-width-strip_ansi", + "symbol": "strip_ansi", + "ownerContext": "module", + "file": "src/click/_compat.py", + "startLine": 491, + "witnessEndLine": 492, + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "witness": "def strip_ansi(value: str) -> str:\n return _ansi_re.sub(\"\", value)" + } + ] + }, + { + "id": "click-help-definition-layout", + "task": "Calculate columns when formatting option and command help.", + "sourceReason": "HelpFormatter.write_dl materializes rows and invokes measure_table to calculate column widths.", + "declarations": [ + { + "id": "click-help-definition-layout-write_dl", + "symbol": "write_dl", + "ownerContext": "HelpFormatter", + "file": "src/click/formatting.py", + "startLine": 229, + "witnessEndLine": 248, + "sourceFileSha256": "f125b628692f8dfcfd43535b7a88cc1ee64137471f9d0243b389aa0cfea85e6b", + "witness": " def write_dl(\n self,\n rows: cabc.Iterable[tuple[str, str]],\n col_max: int = 30,\n col_spacing: int = 2,\n ) -> None:\n \"\"\"Writes a definition list into the buffer. This is how options\n and commands are usually formatted.\n\n :param rows: a list of two item tuples for the terms and values.\n :param col_max: the maximum width of the first column.\n :param col_spacing: the number of spaces between the first and\n second column.\n \"\"\"\n rows = list(rows)\n widths = measure_table(rows)\n if len(widths) != 2:\n raise TypeError(\"Expected two columns for definition list\")\n\n first_col = min(widths[0], col_max) + col_spacing" + }, + { + "id": "click-help-definition-layout-measure_table", + "symbol": "measure_table", + "ownerContext": "module", + "file": "src/click/formatting.py", + "startLine": 14, + "witnessEndLine": 21, + "sourceFileSha256": "f125b628692f8dfcfd43535b7a88cc1ee64137471f9d0243b389aa0cfea85e6b", + "witness": "def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]:\n widths: dict[int, int] = {}\n\n for row in rows:\n for idx, col in enumerate(row):\n widths[idx] = max(widths.get(idx, 0), term_len(col))\n\n return tuple(y for x, y in sorted(widths.items()))" + } + ] + } + ] + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "graphSha256": { + "compass": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "graphify": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + "tasks": [ + { + "id": "jsoup-safe-document-copy", + "task": "Copy allowed body content into a separate clean document.", + "sourceReason": "clean creates a shell and delegates body copying to copySafeNodes; that helper traverses a CleaningVisitor and returns its discarded count.", + "declarations": [ + { + "id": "jsoup-safe-document-copy-clean", + "symbol": "clean", + "ownerContext": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "witnessEndLine": 71, + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "witness": " public Document clean(Document dirtyDocument) {\n Validate.notNull(dirtyDocument);\n\n Document clean = Document.createShell(dirtyDocument.baseUri());\n copySafeNodes(dirtyDocument.body(), clean.body());\n clean.outputSettings(dirtyDocument.outputSettings().clone());\n\n return clean;\n }\n" + }, + { + "id": "jsoup-safe-document-copy-copySafeNodes", + "symbol": "copySafeNodes", + "ownerContext": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "witnessEndLine": 186, + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "witness": " private int copySafeNodes(Element source, Element dest) {\n CleaningVisitor cleaningVisitor = new CleaningVisitor(source, dest);\n cleaningVisitor.traverse(source);\n return cleaningVisitor.numDiscarded;\n }" + } + ] + }, + { + "id": "jsoup-html-fragment-parse", + "task": "Parse an HTML fragment into a document body.", + "sourceReason": "parseBodyFragment constructs the body context and calls the three-argument parseFragment overload, which invokes HtmlTreeBuilder.", + "declarations": [ + { + "id": "jsoup-html-fragment-parse-parseBodyFragment", + "symbol": "parseBodyFragment", + "ownerContext": "Parser", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 338, + "witnessEndLine": 344, + "sourceFileSha256": "2b8baa95140fbf12fad9c874b42d8d31e24b186fd7c665c1708395e6d852ba98", + "witness": " public static Document parseBodyFragment(String bodyHtml, String baseUri) {\n Document doc = Document.createShell(baseUri);\n Element body = doc.body();\n List nodeList = parseFragment(bodyHtml, body, baseUri);\n body.appendChildren(nodeList);\n return doc;\n }" + }, + { + "id": "jsoup-html-fragment-parse-parseFragment", + "symbol": "parseFragment", + "ownerContext": "Parser", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 295, + "witnessEndLine": 298, + "sourceFileSha256": "2b8baa95140fbf12fad9c874b42d8d31e24b186fd7c665c1708395e6d852ba98", + "witness": " public static List parseFragment(String fragmentHtml, Element context, String baseUri) {\n HtmlTreeBuilder treeBuilder = new HtmlTreeBuilder();\n return treeBuilder.parseFragment(new StringReader(fragmentHtml), context, baseUri, new Parser(treeBuilder));\n }" + } + ] + }, + { + "id": "jsoup-html-whitespace", + "task": "Determine whether text consists of HTML whitespace.", + "sourceReason": "isBlank checks each code point through isWhitespace; isWhitespace implements the specific HTML whitespace character set.", + "declarations": [ + { + "id": "jsoup-html-whitespace-isBlank", + "symbol": "isBlank", + "ownerContext": "StringUtil", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 151, + "witnessEndLine": 162, + "sourceFileSha256": "7e61ba7e8630f101f0a74d56fb91237c8588fceeb5c472166c60926dd6047881", + "witness": " public static boolean isBlank(@Nullable String string) {\n if (string == null || string.isEmpty())\n return true;\n\n int l = string.length();\n for (int i = 0; i < l; i++) {\n if (!StringUtil.isWhitespace(string.codePointAt(i)))\n return false;\n }\n return true;\n }\n" + }, + { + "id": "jsoup-html-whitespace-isWhitespace", + "symbol": "isWhitespace", + "ownerContext": "StringUtil", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 205, + "witnessEndLine": 207, + "sourceFileSha256": "7e61ba7e8630f101f0a74d56fb91237c8588fceeb5c472166c60926dd6047881", + "witness": " public static boolean isWhitespace(int c){\n return c == ' ' || c == '\\t' || c == '\\n' || c == '\\f' || c == '\\r';\n }" + } + ] + } + ] + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "graphSha256": { + "compass": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphify": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + "tasks": [ + { + "id": "redux-listener-registration", + "task": "Register and unregister listeners without rewriting the active listener map.", + "sourceReason": "subscribe invokes ensureCanMutateNextListeners before adding and removing listeners; the helper copies the map when it aliases currentListeners.", + "declarations": [ + { + "id": "redux-listener-registration-subscribe", + "symbol": "subscribe", + "ownerContext": "createStore implementation", + "file": "src/createStore.ts", + "startLine": 201, + "witnessEndLine": 243, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "witness": " function subscribe(listener: () => void) {\n if (typeof listener !== 'function') {\n throw new Error(\n `Expected the listener to be a function. Instead, received: '${kindOf(\n listener\n )}'`\n )\n }\n\n if (isDispatching) {\n throw new Error(\n 'You may not call store.subscribe() while the reducer is executing. ' +\n 'If you would like to be notified after the store has been updated, subscribe from a ' +\n 'component and invoke store.getState() in the callback to access the latest state. ' +\n 'See https://redux.js.org/api/store#subscribelistener for more details.'\n )\n }\n\n let isSubscribed = true\n\n ensureCanMutateNextListeners()\n const listenerId = listenerIdCounter++\n nextListeners.set(listenerId, listener)\n\n return function unsubscribe() {\n if (!isSubscribed) {\n return\n }\n\n if (isDispatching) {\n throw new Error(\n 'You may not unsubscribe from a store listener while the reducer is executing. ' +\n 'See https://redux.js.org/api/store#subscribelistener for more details.'\n )\n }\n\n isSubscribed = false\n\n ensureCanMutateNextListeners()\n nextListeners.delete(listenerId)\n currentListeners = null\n }\n }" + }, + { + "id": "redux-listener-registration-ensureCanMutateNextListeners", + "symbol": "ensureCanMutateNextListeners", + "ownerContext": "createStore implementation", + "file": "src/createStore.ts", + "startLine": 152, + "witnessEndLine": 159, + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "witness": " function ensureCanMutateNextListeners() {\n if (nextListeners === currentListeners) {\n nextListeners = new Map()\n currentListeners.forEach((listener, key) => {\n nextListeners.set(key, listener)\n })\n }\n }" + } + ] + }, + { + "id": "redux-action-shape-validation", + "task": "Check that an action is a plain object with a string type.", + "sourceReason": "isAction first calls isPlainObject, then checks the type property; isPlainObject examines the object prototype chain.", + "declarations": [ + { + "id": "redux-action-shape-validation-isAction", + "symbol": "isAction", + "ownerContext": "module", + "file": "src/utils/isAction.ts", + "startLine": 4, + "witnessEndLine": 10, + "sourceFileSha256": "2cf7a3b535cb14b9bb2ac7648b5f103f2a80427d96c7eb391fd11f617944a8a2", + "witness": "export default function isAction(action: unknown): action is Action {\n return (\n isPlainObject(action) &&\n 'type' in action &&\n typeof (action as Record<'type', unknown>).type === 'string'\n )\n}" + }, + { + "id": "redux-action-shape-validation-isPlainObject", + "symbol": "isPlainObject", + "ownerContext": "module", + "file": "src/utils/isPlainObject.ts", + "startLine": 5, + "witnessEndLine": 16, + "sourceFileSha256": "30153dae9fd245b8574f96cf9c08cf33e7d12881b0ad4e4e458646bef5b32793", + "witness": "export default function isPlainObject(obj: any): obj is object {\n if (typeof obj !== 'object' || obj === null) return false\n\n let proto = obj\n while (Object.getPrototypeOf(proto) !== null) {\n proto = Object.getPrototypeOf(proto)\n }\n\n return (\n Object.getPrototypeOf(obj) === proto || Object.getPrototypeOf(obj) === null\n )\n}" + } + ] + }, + { + "id": "redux-action-creator-binding", + "task": "Wrap action creators so their results are dispatched.", + "sourceReason": "The bindActionCreators implementation handles a function or a map and delegates each callable to bindActionCreator, whose wrapper dispatches the returned action.", + "declarations": [ + { + "id": "redux-action-creator-binding-bindActionCreators", + "symbol": "bindActionCreators", + "ownerContext": "module", + "file": "src/bindActionCreators.ts", + "startLine": 58, + "witnessEndLine": 83, + "sourceFileSha256": "53b834cc532443fcf9f392ea86b970b640bccea5dac7c2ef16516cbe99a176f1", + "witness": "export default function bindActionCreators(\n actionCreators: ActionCreator | ActionCreatorsMapObject,\n dispatch: Dispatch\n) {\n if (typeof actionCreators === 'function') {\n return bindActionCreator(actionCreators, dispatch)\n }\n\n if (typeof actionCreators !== 'object' || actionCreators === null) {\n throw new Error(\n `bindActionCreators expected an object or a function, but instead received: '${kindOf(\n actionCreators\n )}'. ` +\n `Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?`\n )\n }\n\n const boundActionCreators: ActionCreatorsMapObject = {}\n for (const key in actionCreators) {\n const actionCreator = actionCreators[key]\n if (typeof actionCreator === 'function') {\n boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)\n }\n }\n return boundActionCreators\n}" + }, + { + "id": "redux-action-creator-binding-bindActionCreator", + "symbol": "bindActionCreator", + "ownerContext": "module", + "file": "src/bindActionCreators.ts", + "startLine": 9, + "witnessEndLine": 16, + "sourceFileSha256": "53b834cc532443fcf9f392ea86b970b640bccea5dac7c2ef16516cbe99a176f1", + "witness": "function bindActionCreator(\n actionCreator: ActionCreator,\n dispatch: Dispatch\n) {\n return function (this: any, ...args: any[]) {\n return dispatch(actionCreator.apply(this, args))\n }\n}" + } + ] + } + ] + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "graphSha256": { + "compass": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177", + "graphify": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + }, + "tasks": [ + { + "id": "walkdir-directory-handle-budget", + "task": "Make room under the open-directory limit before opening another directory.", + "sourceReason": "IntoIter.push closes the oldest open DirList when max_open is reached. DirList.close drains an open stream into a closed in-memory iterator.", + "declarations": [ + { + "id": "walkdir-directory-handle-budget-push", + "symbol": "push", + "ownerContext": "IntoIter", + "file": "src/lib.rs", + "startLine": 901, + "witnessEndLine": 914, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn push(&mut self, dent: &DirEntry) -> Result<()> {\n // Make room for another open file descriptor if we've hit the max.\n let free =\n self.stack_list.len().checked_sub(self.oldest_opened).unwrap();\n if free == self.opts.max_open {\n self.stack_list[self.oldest_opened].close();\n }\n // Open a handle to reading the directory's entries.\n let rd = fs::read_dir(dent.path()).map_err(|err| {\n Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))\n });\n let mut list = DirList::Opened { depth: self.depth, it: rd };\n if let Some(ref mut cmp) = self.opts.sorter {\n let mut entries: Vec<_> = list.collect();" + }, + { + "id": "walkdir-directory-handle-budget-close", + "symbol": "close", + "ownerContext": "DirList", + "file": "src/lib.rs", + "startLine": 1008, + "witnessEndLine": 1012, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn close(&mut self) {\n if let DirList::Opened { .. } = *self {\n *self = DirList::Closed(self.collect::>().into_iter());\n }\n }" + } + ] + }, + { + "id": "walkdir-deferred-directory-depth", + "task": "Return deferred directory entries only at allowed depths.", + "sourceReason": "get_deferred_dir uses contents_first and the deferred stack, and asks skippable whether the current depth lies outside the configured bounds.", + "declarations": [ + { + "id": "walkdir-deferred-directory-depth-get_deferred_dir", + "symbol": "get_deferred_dir", + "ownerContext": "IntoIter", + "file": "src/lib.rs", + "startLine": 884, + "witnessEndLine": 898, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn get_deferred_dir(&mut self) -> Option {\n if self.opts.contents_first {\n if self.depth < self.deferred_dirs.len() {\n // Unwrap is safe here because we've guaranteed that\n // `self.deferred_dirs.len()` can never be less than 1\n let deferred: DirEntry = self\n .deferred_dirs\n .pop()\n .expect(\"BUG: deferred_dirs should be non-empty\");\n if !self.skippable() {\n return Some(deferred);\n }\n }\n }\n None" + }, + { + "id": "walkdir-deferred-directory-depth-skippable", + "symbol": "skippable", + "ownerContext": "IntoIter", + "file": "src/lib.rs", + "startLine": 1000, + "witnessEndLine": 1002, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn skippable(&self) -> bool {\n self.depth < self.opts.min_depth || self.depth > self.opts.max_depth\n }" + } + ] + }, + { + "id": "walkdir-symlink-loop-detection", + "task": "Check followed directory targets against ancestor handles.", + "sourceReason": "follow invokes check_loop only for directories; check_loop compares handles with ancestors and returns a loop error for a match.", + "declarations": [ + { + "id": "walkdir-symlink-loop-detection-follow", + "symbol": "follow", + "ownerContext": "IntoIter", + "file": "src/lib.rs", + "startLine": 961, + "witnessEndLine": 971, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn follow(&self, mut dent: DirEntry) -> Result {\n dent =\n DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;\n // The only way a symlink can cause a loop is if it points\n // to a directory. Otherwise, it always points to a leaf\n // and we can omit any loop checks.\n if dent.is_dir() {\n self.check_loop(dent.path())?;\n }\n Ok(dent)\n }" + }, + { + "id": "walkdir-symlink-loop-detection-check_loop", + "symbol": "check_loop", + "ownerContext": "IntoIter", + "file": "src/lib.rs", + "startLine": 973, + "witnessEndLine": 989, + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "witness": " fn check_loop>(&self, child: P) -> Result<()> {\n let hchild = Handle::from_path(&child)\n .map_err(|err| Error::from_io(self.depth, err))?;\n for ancestor in self.stack_path.iter().rev() {\n let is_same = ancestor\n .is_same(&hchild)\n .map_err(|err| Error::from_io(self.depth, err))?;\n if is_same {\n return Err(Error::from_loop(\n self.depth,\n &ancestor.path,\n child.as_ref(),\n ));\n }\n }\n Ok(())\n }" + } + ] + } + ] + } + ] +} From bef1d765ec9a60c981d5757a015481af692386f8 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:24:53 -0700 Subject: [PATCH 47/97] test: audit source-defined community task cohesion and boundaries --- benchmarks/agent_query/COVERAGE_PLAN.md | 19 + benchmarks/agent_query/README.md | 28 + .../community_task_pairs_review_panel_a.json | 1478 +++++++++++++++++ benchmarks/agent_query/community_tasks.py | 213 +++ .../agent_query/tests/test_community_tasks.py | 130 ++ ...ode-graph-intelligence-audit-2026-09-26.md | 53 + 6 files changed, 1921 insertions(+) create mode 100644 benchmarks/agent_query/community_task_pairs_review_panel_a.json create mode 100644 benchmarks/agent_query/community_tasks.py create mode 100644 benchmarks/agent_query/tests/test_community_tasks.py diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index eee872853..bcaf2949b 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -112,6 +112,25 @@ summary is unavailable in that response, not an incorrect answer; neither a neighbor follow-up workflow nor Graphify's separate CLI is excluded by this finding. Do not turn summary availability into a cross-tool accuracy score. +### Source-defined community task pairs + +`community_task_pairs_panel_a.json` freezes 30 declarations and their source +mechanisms in commit `234753eb`, before this task-pair membership audit. Each +repository has three task pairs. `community_tasks.py` audits all 15 within-task +and 60 cross-task pairs per tool on the existing native graph artifacts. +It requires exact declaration starts and names; missing, ambiguous and +unassigned endpoints stay unresolved. Invalid identities and exceeded bounds +are errors. + +`community_task_pairs_review_panel_a.json` records 13/15 collaborator pairs +co-located for Compass and 12/15 for Graphify. Cross-task co-location is 18/60 +and 12/60. These are separate granularity observations, not a combined quality +score. All five split collaborator pairs have the source-supported call edge. +Click's help formatting deliberately uses its terminal-width helper, and +WalkDir's different tasks share one iterator: cross-task grouping alone is not +a design defect. Native boundary navigation, broader source responsibilities, +independent review and god-object labels remain open. + ### Responsibility explanation evidence `responsibility_questions_panel_a.json` freezes five questions and 20 source diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index c7c7c32de..0e1912b35 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -111,6 +111,34 @@ packages. Use an immutable environment when comparing installations. ## Metrics +### Source-defined community task diagnostic + +`community_task_pairs_panel_a.json` records 30 source declarations grouped into +15 collaborator pairs across Chi, Click, jsoup, Redux and WalkDir. Run the +artifact audit against the registered `java-varargs-panel-a-02` capture: + +```bash +python3 -m benchmarks.agent_query.community_tasks \ + --registration benchmarks/agent_query/community_task_pairs_panel_a.json \ + --run /path/to/java-varargs-panel-a-02/run.json \ + --output /path/to/new-community-audit.json +``` + +The output path must be new. The auditor verifies source commits, file hashes, +exact witness text and graph hashes, then records exact declaration identity +and community membership. Missing or ambiguous anchors cannot pass as separate +communities. It reports within-task co-location separately from cross-task +co-location, including same-file and cross-file strata. Community IDs are never +compared across tools. This measures grouping granularity on a small development +panel, not native answer quality, ideal architecture or god-object defects. + +The [review](community_task_pairs_review_panel_a.json) retains every selected +declaration and the five source-supported calls crossing community boundaries. +Ten focused auditor tests cover ambiguity, missing assignments, identity, +bounds, zero-valued community IDs and ordering invariance. + +### Query metrics + - **Correctness**: stdout with an accepted exit status and without timeout or output-limit failure is judged against the suite's anchors. Graphify `explain` deliberately returns exit 1 for ambiguity: that status is diff --git a/benchmarks/agent_query/community_task_pairs_review_panel_a.json b/benchmarks/agent_query/community_task_pairs_review_panel_a.json new file mode 100644 index 000000000..fa42231ae --- /dev/null +++ b/benchmarks/agent_query/community_task_pairs_review_panel_a.json @@ -0,0 +1,1478 @@ +{ + "schema": "compass.community-task-pairs-review/1", + "registrationCommit": "234753eb", + "scope": "Source-defined task co-location and granularity on captured native-only graph artifacts. The registration was frozen before this task-pair membership audit; earlier broad comparisons had already exposed portions of these graphs. This is a development diagnostic, not an independent reviewer, native answer workflow, architecture oracle, or god-object evaluation.", + "artifactRoot": "community-task-pairs-01", + "captureFile": "capture-final.json", + "captureSha256": "2924f3f7b8e66e2a1e14562ff6e0ed6c2a87208ec3da3ee7eee172d0d6c43889", + "initialCaptureSha256": "d0e0c564328a74a9282a5a44c420b6c91625fd7def51da3ecee2c801e00cc3a8", + "registrationSha256": "71faed0f477ee97da98c8bbb86686fae09de450664e4e000d9b2404496b94b6d", + "sourceRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "auditorSha256": "77f599ff7ee7eebfe4b590fee6c771ecfb3ed475d1944e37bcabc110634d3660", + "anchorMatcherSha256": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "summaryRecheckSha256": "abf4b4d6496ef27b83aeefeaae4b767b1c3f5d6dfedd3bfe8e7755947d1b813b", + "verification": { + "command": "python3 -m unittest discover -s benchmarks/agent_query/tests -v", + "passed": 109, + "failed": 0, + "logSha256": "a72c008df7dd7e2eb811b1097d415417dc52744315ae471f63c6ef788cc26ca3", + "notes": "Includes ten new community audit tests. Final auditor repeats every declaration resolution and pair outcome from the first capture. A separate direct recomputation checked all source anchors and pair outcomes; it is not an independent semantic review. Rust/product tests were not rerun because this iteration changes only developer-side Python evaluation and documentation." + }, + "summaries": { + "compass": { + "cross_task": { + "same_community": 18, + "different_community": 42, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 6, + "different_community": 38, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 13, + "different_community": 2, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 11, + "different_community": 2, + "unresolved": 0 + } + }, + "graphify": { + "cross_task": { + "same_community": 12, + "different_community": 48, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 44, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 12, + "different_community": 3, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 10, + "different_community": 3, + "unresolved": 0 + } + } + }, + "repositories": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "tools": { + "compass": { + "nodes": 729, + "assignedNodes": 729, + "communities": 154, + "declarations": [ + { + "id": "chi-request-identity-GetReqID", + "task": "chi-request-identity", + "file": "middleware/request_id.go", + "startLine": 83, + "symbol": "GetReqID", + "matchedNodeIds": [ + "sha256:34078dccfae1ef1443550189102e7927d10009b45d211741895af2b59e44907e" + ], + "status": "resolved", + "community": 3, + "communityNodes": 44, + "communitySourceFiles": 9 + }, + { + "id": "chi-request-identity-RequestID", + "task": "chi-request-identity", + "file": "middleware/request_id.go", + "startLine": 67, + "symbol": "RequestID", + "matchedNodeIds": [ + "sha256:394032841e0823b74d28b8a866c2eb3cd7618a345c06b08fc66e63e5657d5ae4" + ], + "status": "resolved", + "community": 3, + "communityNodes": 44, + "communitySourceFiles": 9 + }, + { + "id": "chi-route-pattern-parameters-patNextSegment", + "task": "chi-route-pattern-parameters", + "file": "tree.go", + "startLine": 735, + "symbol": "patNextSegment", + "matchedNodeIds": [ + "sha256:111dda918f957e920843303a5c69f2cda3e9b2006d711b0aef4efca1545bdc62" + ], + "status": "resolved", + "community": 4, + "communityNodes": 41, + "communitySourceFiles": 3 + }, + { + "id": "chi-route-pattern-parameters-patParamKeys", + "task": "chi-route-pattern-parameters", + "file": "tree.go", + "startLine": 803, + "symbol": "patParamKeys", + "matchedNodeIds": [ + "sha256:6df5a320c50c2a4cc16144d1db213eeefc7cf84eced0fd9b1fbedcd21553b212" + ], + "status": "resolved", + "community": 4, + "communityNodes": 41, + "communitySourceFiles": 3 + }, + { + "id": "chi-router-construction-NewMux", + "task": "chi-router-construction", + "file": "mux.go", + "startLine": 52, + "symbol": "NewMux", + "matchedNodeIds": [ + "sha256:fd36852628b4b3873d779bdb534cf23a17b6908247f6c46db80dbac5b2d7c453" + ], + "status": "resolved", + "community": 0, + "communityNodes": 90, + "communitySourceFiles": 8 + }, + { + "id": "chi-router-construction-NewRouter", + "task": "chi-router-construction", + "file": "chi.go", + "startLine": 62, + "symbol": "NewRouter", + "matchedNodeIds": [ + "sha256:1928266631ebdc4651cac18aa5abc7ab261abc74a669ec9071a53d68b1f30699" + ], + "status": "resolved", + "community": 0, + "communityNodes": 90, + "communitySourceFiles": 8 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 1, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5" + }, + "graphify": { + "nodes": 674, + "assignedNodes": 674, + "communities": 33, + "declarations": [ + { + "id": "chi-request-identity-GetReqID", + "task": "chi-request-identity", + "file": "middleware/request_id.go", + "startLine": 83, + "symbol": "GetReqID", + "matchedNodeIds": [ + "middleware_request_id_getreqid" + ], + "status": "resolved", + "community": 5, + "communityNodes": 58, + "communitySourceFiles": 11 + }, + { + "id": "chi-request-identity-RequestID", + "task": "chi-request-identity", + "file": "middleware/request_id.go", + "startLine": 67, + "symbol": "RequestID", + "matchedNodeIds": [ + "middleware_request_id_requestid" + ], + "status": "resolved", + "community": 5, + "communityNodes": 58, + "communitySourceFiles": 11 + }, + { + "id": "chi-route-pattern-parameters-patNextSegment", + "task": "chi-route-pattern-parameters", + "file": "tree.go", + "startLine": 735, + "symbol": "patNextSegment", + "matchedNodeIds": [ + "tree_patnextsegment" + ], + "status": "resolved", + "community": 7, + "communityNodes": 42, + "communitySourceFiles": 4 + }, + { + "id": "chi-route-pattern-parameters-patParamKeys", + "task": "chi-route-pattern-parameters", + "file": "tree.go", + "startLine": 803, + "symbol": "patParamKeys", + "matchedNodeIds": [ + "tree_patparamkeys" + ], + "status": "resolved", + "community": 7, + "communityNodes": 42, + "communitySourceFiles": 4 + }, + { + "id": "chi-router-construction-NewMux", + "task": "chi-router-construction", + "file": "mux.go", + "startLine": 52, + "symbol": "NewMux", + "matchedNodeIds": [ + "mux_newmux" + ], + "status": "resolved", + "community": 0, + "communityNodes": 108, + "communitySourceFiles": 21 + }, + { + "id": "chi-router-construction-NewRouter", + "task": "chi-router-construction", + "file": "chi.go", + "startLine": 62, + "symbol": "NewRouter", + "matchedNodeIds": [ + "chi_newrouter" + ], + "status": "resolved", + "community": 0, + "communityNodes": 108, + "communitySourceFiles": 21 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 1, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + } + } + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "tools": { + "compass": { + "nodes": 4264, + "assignedNodes": 4264, + "communities": 173, + "declarations": [ + { + "id": "click-atomic-close-__exit__", + "task": "click-atomic-close", + "file": "src/click/_compat.py", + "startLine": 479, + "symbol": "__exit__", + "matchedNodeIds": [ + "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4" + ], + "status": "resolved", + "community": 6, + "communityNodes": 203, + "communitySourceFiles": 6 + }, + { + "id": "click-atomic-close-close", + "task": "click-atomic-close", + "file": "src/click/_compat.py", + "startLine": 466, + "symbol": "close", + "matchedNodeIds": [ + "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3" + ], + "status": "resolved", + "community": 6, + "communityNodes": 203, + "communitySourceFiles": 6 + }, + { + "id": "click-help-definition-layout-measure_table", + "task": "click-help-definition-layout", + "file": "src/click/formatting.py", + "startLine": 14, + "symbol": "measure_table", + "matchedNodeIds": [ + "sha256:8e5a66041c4ec1628dcf55670a1c379436eec6a47f92a8a45608693bc2934e5e" + ], + "status": "resolved", + "community": 15, + "communityNodes": 95, + "communitySourceFiles": 4 + }, + { + "id": "click-help-definition-layout-write_dl", + "task": "click-help-definition-layout", + "file": "src/click/formatting.py", + "startLine": 229, + "symbol": "write_dl", + "matchedNodeIds": [ + "sha256:3883ba20e7d4cfe17c813110a793c1d9db32455194d42971a01787a2b45c357e" + ], + "status": "resolved", + "community": 15, + "communityNodes": 95, + "communitySourceFiles": 4 + }, + { + "id": "click-terminal-string-width-strip_ansi", + "task": "click-terminal-string-width", + "file": "src/click/_compat.py", + "startLine": 491, + "symbol": "strip_ansi", + "matchedNodeIds": [ + "sha256:84776cf5b684f5564ad6dc1143881454a6dd6f12faf2f62a8b1451fc9c89dbd2" + ], + "status": "resolved", + "community": 3, + "communityNodes": 246, + "communitySourceFiles": 9 + }, + { + "id": "click-terminal-string-width-term_len", + "task": "click-terminal-string-width", + "file": "src/click/_compat.py", + "startLine": 536, + "symbol": "term_len", + "matchedNodeIds": [ + "sha256:b11122339a79629b59028324039bb74faf1857f56629c753c1e51abc3c93d330" + ], + "status": "resolved", + "community": 15, + "communityNodes": 95, + "communitySourceFiles": 4 + } + ], + "summaries": { + "cross_task": { + "same_community": 2, + "different_community": 10, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 2, + "different_community": 6, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 0, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 2, + "different_community": 1, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 1, + "unresolved": 0 + } + }, + "graphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93" + }, + "graphify": { + "nodes": 2867, + "assignedNodes": 2867, + "communities": 204, + "declarations": [ + { + "id": "click-atomic-close-__exit__", + "task": "click-atomic-close", + "file": "src/click/_compat.py", + "startLine": 479, + "symbol": "__exit__", + "matchedNodeIds": [ + "src_click_compat_atomicfile_exit" + ], + "status": "resolved", + "community": 86, + "communityNodes": 9, + "communitySourceFiles": 1 + }, + { + "id": "click-atomic-close-close", + "task": "click-atomic-close", + "file": "src/click/_compat.py", + "startLine": 466, + "symbol": "close", + "matchedNodeIds": [ + "src_click_compat_atomicfile_close" + ], + "status": "resolved", + "community": 86, + "communityNodes": 9, + "communitySourceFiles": 1 + }, + { + "id": "click-help-definition-layout-measure_table", + "task": "click-help-definition-layout", + "file": "src/click/formatting.py", + "startLine": 14, + "symbol": "measure_table", + "matchedNodeIds": [ + "src_click_formatting_measure_table" + ], + "status": "resolved", + "community": 87, + "communityNodes": 9, + "communitySourceFiles": 1 + }, + { + "id": "click-help-definition-layout-write_dl", + "task": "click-help-definition-layout", + "file": "src/click/formatting.py", + "startLine": 229, + "symbol": "write_dl", + "matchedNodeIds": [ + "src_click_formatting_helpformatter_write_dl" + ], + "status": "resolved", + "community": 87, + "communityNodes": 9, + "communitySourceFiles": 1 + }, + { + "id": "click-terminal-string-width-strip_ansi", + "task": "click-terminal-string-width", + "file": "src/click/_compat.py", + "startLine": 491, + "symbol": "strip_ansi", + "matchedNodeIds": [ + "src_click_compat_strip_ansi" + ], + "status": "resolved", + "community": 45, + "communityNodes": 24, + "communitySourceFiles": 3 + }, + { + "id": "click-terminal-string-width-term_len", + "task": "click-terminal-string-width", + "file": "src/click/_compat.py", + "startLine": 536, + "symbol": "term_len", + "matchedNodeIds": [ + "src_click_compat_term_len" + ], + "status": "resolved", + "community": 45, + "communityNodes": 24, + "communitySourceFiles": 3 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 8, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 0, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + } + } + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "tools": { + "compass": { + "nodes": 6116, + "assignedNodes": 6116, + "communities": 41, + "declarations": [ + { + "id": "jsoup-html-fragment-parse-parseBodyFragment", + "task": "jsoup-html-fragment-parse", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 338, + "symbol": "parseBodyFragment", + "matchedNodeIds": [ + "sha256:116e34df3ea1beb0e4e87a6fbc8c2a4667bcac6135618616b616a6990d10e729" + ], + "status": "resolved", + "community": 0, + "communityNodes": 815, + "communitySourceFiles": 55 + }, + { + "id": "jsoup-html-fragment-parse-parseFragment", + "task": "jsoup-html-fragment-parse", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 295, + "symbol": "parseFragment", + "matchedNodeIds": [ + "sha256:d51486603fd23ace9d94cf199d726965909177f01ed78bd3ed9a3ca39436412b" + ], + "status": "resolved", + "community": 0, + "communityNodes": 815, + "communitySourceFiles": 55 + }, + { + "id": "jsoup-html-whitespace-isBlank", + "task": "jsoup-html-whitespace", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 151, + "symbol": "isBlank", + "matchedNodeIds": [ + "sha256:7b70b1683af87dcfb443f8d622d324166f6a6a9d6d40e66b0f8baf1eea8ebbd7" + ], + "status": "resolved", + "community": 17, + "communityNodes": 143, + "communitySourceFiles": 19 + }, + { + "id": "jsoup-html-whitespace-isWhitespace", + "task": "jsoup-html-whitespace", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 205, + "symbol": "isWhitespace", + "matchedNodeIds": [ + "sha256:8b362a6fd1d3e3687e87d8010b0acf4af64a9bdf2c0cbd1ba51760278afb3227" + ], + "status": "resolved", + "community": 12, + "communityNodes": 191, + "communitySourceFiles": 23 + }, + { + "id": "jsoup-safe-document-copy-clean", + "task": "jsoup-safe-document-copy", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "symbol": "clean", + "matchedNodeIds": [ + "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034" + ], + "status": "resolved", + "community": 15, + "communityNodes": 159, + "communitySourceFiles": 19 + }, + { + "id": "jsoup-safe-document-copy-copySafeNodes", + "task": "jsoup-safe-document-copy", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "symbol": "copySafeNodes", + "matchedNodeIds": [ + "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3" + ], + "status": "resolved", + "community": 15, + "communityNodes": 159, + "communitySourceFiles": 19 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "within_task": { + "same_community": 2, + "different_community": 1, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 1, + "unresolved": 0 + } + }, + "graphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead" + }, + "graphify": { + "nodes": 5361, + "assignedNodes": 5361, + "communities": 151, + "declarations": [ + { + "id": "jsoup-html-fragment-parse-parseBodyFragment", + "task": "jsoup-html-fragment-parse", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 338, + "symbol": "parseBodyFragment", + "matchedNodeIds": [ + "src_main_java_org_jsoup_parser_parser_parser_parsebodyfragment" + ], + "status": "resolved", + "community": 1, + "communityNodes": 232, + "communitySourceFiles": 23 + }, + { + "id": "jsoup-html-fragment-parse-parseFragment", + "task": "jsoup-html-fragment-parse", + "file": "src/main/java/org/jsoup/parser/Parser.java", + "startLine": 295, + "symbol": "parseFragment", + "matchedNodeIds": [ + "src_main_java_org_jsoup_parser_parser_parser_parsefragment" + ], + "status": "resolved", + "community": 39, + "communityNodes": 36, + "communitySourceFiles": 10 + }, + { + "id": "jsoup-html-whitespace-isBlank", + "task": "jsoup-html-whitespace", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 151, + "symbol": "isBlank", + "matchedNodeIds": [ + "src_main_java_org_jsoup_internal_stringutil_stringutil_isblank" + ], + "status": "resolved", + "community": 16, + "communityNodes": 76, + "communitySourceFiles": 5 + }, + { + "id": "jsoup-html-whitespace-isWhitespace", + "task": "jsoup-html-whitespace", + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startLine": 205, + "symbol": "isWhitespace", + "matchedNodeIds": [ + "src_main_java_org_jsoup_internal_stringutil_stringutil_iswhitespace" + ], + "status": "resolved", + "community": 48, + "communityNodes": 31, + "communitySourceFiles": 4 + }, + { + "id": "jsoup-safe-document-copy-clean", + "task": "jsoup-safe-document-copy", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "symbol": "clean", + "matchedNodeIds": [ + "src_main_java_org_jsoup_safety_cleaner_cleaner_clean" + ], + "status": "resolved", + "community": 24, + "communityNodes": 56, + "communitySourceFiles": 5 + }, + { + "id": "jsoup-safe-document-copy-copySafeNodes", + "task": "jsoup-safe-document-copy", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "symbol": "copySafeNodes", + "matchedNodeIds": [ + "src_main_java_org_jsoup_safety_cleaner_cleaner_copysafenodes" + ], + "status": "resolved", + "community": 129, + "communityNodes": 7, + "communitySourceFiles": 3 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "within_task": { + "same_community": 0, + "different_community": 3, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 0, + "different_community": 3, + "unresolved": 0 + } + }, + "graphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + } + } + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "tools": { + "compass": { + "nodes": 3503, + "assignedNodes": 3503, + "communities": 845, + "declarations": [ + { + "id": "redux-action-creator-binding-bindActionCreator", + "task": "redux-action-creator-binding", + "file": "src/bindActionCreators.ts", + "startLine": 9, + "symbol": "bindActionCreator", + "matchedNodeIds": [ + "sha256:118ac793f4d6b77676a6543f79b8cdef6cd785fef2e27dcaee49c6568ff3488d" + ], + "status": "resolved", + "community": 15, + "communityNodes": 55, + "communitySourceFiles": 3 + }, + { + "id": "redux-action-creator-binding-bindActionCreators", + "task": "redux-action-creator-binding", + "file": "src/bindActionCreators.ts", + "startLine": 58, + "symbol": "bindActionCreators", + "matchedNodeIds": [ + "sha256:872a5ee2bce2daedae73fa970870b29f3d0e02033199aa263da175827b5899fe" + ], + "status": "resolved", + "community": 15, + "communityNodes": 55, + "communitySourceFiles": 3 + }, + { + "id": "redux-action-shape-validation-isAction", + "task": "redux-action-shape-validation", + "file": "src/utils/isAction.ts", + "startLine": 4, + "symbol": "isAction", + "matchedNodeIds": [ + "sha256:237b20392b78b4972302af1924657a5709e90d5534fbdaee1ac633567db87377" + ], + "status": "resolved", + "community": 1, + "communityNodes": 252, + "communitySourceFiles": 14 + }, + { + "id": "redux-action-shape-validation-isPlainObject", + "task": "redux-action-shape-validation", + "file": "src/utils/isPlainObject.ts", + "startLine": 5, + "symbol": "isPlainObject", + "matchedNodeIds": [ + "sha256:08c1f8776bb6ac1c79c535fe70f0341ba85fb12fcf8ab56171ee2281e3f6fcb9" + ], + "status": "resolved", + "community": 1, + "communityNodes": 252, + "communitySourceFiles": 14 + }, + { + "id": "redux-listener-registration-ensureCanMutateNextListeners", + "task": "redux-listener-registration", + "file": "src/createStore.ts", + "startLine": 152, + "symbol": "ensureCanMutateNextListeners", + "matchedNodeIds": [ + "sha256:692fe8cbc7b8e5a03bcb79c20531aefdbfa31098ea43bbe9b0301d61cd869f27" + ], + "status": "resolved", + "community": 15, + "communityNodes": 55, + "communitySourceFiles": 3 + }, + { + "id": "redux-listener-registration-subscribe", + "task": "redux-listener-registration", + "file": "src/createStore.ts", + "startLine": 201, + "symbol": "subscribe", + "matchedNodeIds": [ + "sha256:f27566baebab97a78ea7b6d143149ecdff76b39167fb12b42612a3e69fba5a5d" + ], + "status": "resolved", + "community": 15, + "communityNodes": 55, + "communitySourceFiles": 3 + } + ], + "summaries": { + "cross_task": { + "same_community": 4, + "different_community": 8, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 4, + "different_community": 8, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 1, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b" + }, + "graphify": { + "nodes": 996, + "assignedNodes": 996, + "communities": 65, + "declarations": [ + { + "id": "redux-action-creator-binding-bindActionCreator", + "task": "redux-action-creator-binding", + "file": "src/bindActionCreators.ts", + "startLine": 9, + "symbol": "bindActionCreator", + "matchedNodeIds": [ + "src_bindactioncreators_bindactioncreator" + ], + "status": "resolved", + "community": 28, + "communityNodes": 14, + "communitySourceFiles": 5 + }, + { + "id": "redux-action-creator-binding-bindActionCreators", + "task": "redux-action-creator-binding", + "file": "src/bindActionCreators.ts", + "startLine": 58, + "symbol": "bindActionCreators", + "matchedNodeIds": [ + "src_bindactioncreators_bindactioncreators" + ], + "status": "resolved", + "community": 28, + "communityNodes": 14, + "communitySourceFiles": 5 + }, + { + "id": "redux-action-shape-validation-isAction", + "task": "redux-action-shape-validation", + "file": "src/utils/isAction.ts", + "startLine": 4, + "symbol": "isAction", + "matchedNodeIds": [ + "src_utils_isaction_isaction" + ], + "status": "resolved", + "community": 43, + "communityNodes": 8, + "communitySourceFiles": 4 + }, + { + "id": "redux-action-shape-validation-isPlainObject", + "task": "redux-action-shape-validation", + "file": "src/utils/isPlainObject.ts", + "startLine": 5, + "symbol": "isPlainObject", + "matchedNodeIds": [ + "src_utils_isplainobject_isplainobject" + ], + "status": "resolved", + "community": 43, + "communityNodes": 8, + "communitySourceFiles": 4 + }, + { + "id": "redux-listener-registration-ensureCanMutateNextListeners", + "task": "redux-listener-registration", + "file": "src/createStore.ts", + "startLine": 152, + "symbol": "ensureCanMutateNextListeners", + "matchedNodeIds": [ + "src_createstore_createstore_ensurecanmutatenextlisteners" + ], + "status": "resolved", + "community": 25, + "communityNodes": 15, + "communitySourceFiles": 2 + }, + { + "id": "redux-listener-registration-subscribe", + "task": "redux-listener-registration", + "file": "src/createStore.ts", + "startLine": 201, + "symbol": "subscribe", + "matchedNodeIds": [ + "src_createstore_createstore_subscribe" + ], + "status": "resolved", + "community": 25, + "communityNodes": 15, + "communitySourceFiles": 2 + } + ], + "summaries": { + "cross_task": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 12, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 1, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + } + } + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "tools": { + "compass": { + "nodes": 288, + "assignedNodes": 288, + "communities": 10, + "declarations": [ + { + "id": "walkdir-deferred-directory-depth-get_deferred_dir", + "task": "walkdir-deferred-directory-depth", + "file": "src/lib.rs", + "startLine": 884, + "symbol": "get_deferred_dir", + "matchedNodeIds": [ + "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + }, + { + "id": "walkdir-deferred-directory-depth-skippable", + "task": "walkdir-deferred-directory-depth", + "file": "src/lib.rs", + "startLine": 1000, + "symbol": "skippable", + "matchedNodeIds": [ + "sha256:648e4f3a42dcd72aa98ad5725459ca59362c53374b257faa9cd00a5cfee708f0" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + }, + { + "id": "walkdir-directory-handle-budget-close", + "task": "walkdir-directory-handle-budget", + "file": "src/lib.rs", + "startLine": 1008, + "symbol": "close", + "matchedNodeIds": [ + "sha256:ff9fee2d67be76e5af714ef0bceb4d68be41efd8086deccc6a46eb19cd9ee0f9" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + }, + { + "id": "walkdir-directory-handle-budget-push", + "task": "walkdir-directory-handle-budget", + "file": "src/lib.rs", + "startLine": 901, + "symbol": "push", + "matchedNodeIds": [ + "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + }, + { + "id": "walkdir-symlink-loop-detection-check_loop", + "task": "walkdir-symlink-loop-detection", + "file": "src/lib.rs", + "startLine": 973, + "symbol": "check_loop", + "matchedNodeIds": [ + "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + }, + { + "id": "walkdir-symlink-loop-detection-follow", + "task": "walkdir-symlink-loop-detection", + "file": "src/lib.rs", + "startLine": 961, + "symbol": "follow", + "matchedNodeIds": [ + "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78" + ], + "status": "resolved", + "community": 0, + "communityNodes": 116, + "communitySourceFiles": 4 + } + ], + "summaries": { + "cross_task": { + "same_community": 12, + "different_community": 0, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 0, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "4ba0ea8d0dc4525afda690739b0261c5b61b0b9dd9e725a338d7f40f204bf177" + }, + "graphify": { + "nodes": 247, + "assignedNodes": 247, + "communities": 15, + "declarations": [ + { + "id": "walkdir-deferred-directory-depth-get_deferred_dir", + "task": "walkdir-deferred-directory-depth", + "file": "src/lib.rs", + "startLine": 884, + "symbol": "get_deferred_dir", + "matchedNodeIds": [ + "src_lib_intoiter_get_deferred_dir" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + }, + { + "id": "walkdir-deferred-directory-depth-skippable", + "task": "walkdir-deferred-directory-depth", + "file": "src/lib.rs", + "startLine": 1000, + "symbol": "skippable", + "matchedNodeIds": [ + "src_lib_intoiter_skippable" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + }, + { + "id": "walkdir-directory-handle-budget-close", + "task": "walkdir-directory-handle-budget", + "file": "src/lib.rs", + "startLine": 1008, + "symbol": "close", + "matchedNodeIds": [ + "src_lib_dirlist_close" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + }, + { + "id": "walkdir-directory-handle-budget-push", + "task": "walkdir-directory-handle-budget", + "file": "src/lib.rs", + "startLine": 901, + "symbol": "push", + "matchedNodeIds": [ + "src_lib_intoiter_push" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + }, + { + "id": "walkdir-symlink-loop-detection-check_loop", + "task": "walkdir-symlink-loop-detection", + "file": "src/lib.rs", + "startLine": 973, + "symbol": "check_loop", + "matchedNodeIds": [ + "src_lib_intoiter_check_loop" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + }, + { + "id": "walkdir-symlink-loop-detection-follow", + "task": "walkdir-symlink-loop-detection", + "file": "src/lib.rs", + "startLine": 961, + "symbol": "follow", + "matchedNodeIds": [ + "src_lib_intoiter_follow" + ], + "status": "resolved", + "community": 2, + "communityNodes": 33, + "communitySourceFiles": 1 + } + ], + "summaries": { + "cross_task": { + "same_community": 12, + "different_community": 0, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 0, + "unresolved": 0 + }, + "within_task": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 3, + "different_community": 0, + "unresolved": 0 + } + }, + "graphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + } + } + ], + "splitPairDiagnostics": [ + { + "repository": "click", + "tool": "compass", + "caller": "term_len", + "callee": "strip_ansi", + "callerCommunity": 15, + "calleeCommunity": 3, + "file": "src/click/_compat.py", + "line": 537, + "sourceLine": " return len(strip_ansi(x))", + "storedEdge": { + "id": "sha256:18a10a498e2a6812c90b68b2319895923078a4da235b181795f53c89be7aee56", + "key": "sha256:18a10a498e2a6812c90b68b2319895923078a4da235b181795f53c89be7aee56", + "source": "sha256:b11122339a79629b59028324039bb74faf1857f56629c753c1e51abc3c93d330", + "target": "sha256:84776cf5b684f5564ad6dc1143881454a6dd6f12faf2f62a8b1451fc9c89dbd2", + "kind": "calls", + "occurrenceRule": "universal-call-exact-lexical-declaration", + "relationshipSite": { + "file": "src/click/_compat.py", + "startByte": 16553, + "endByte": 16563, + "startLine": 537, + "startColumn": 15, + "endLine": 537, + "endColumn": 25 + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.resolve.python.universal", + "confidence": "exact", + "rule": "universal-call-exact-lexical-declaration", + "anchors": [ + { + "file": "src/click/_compat.py", + "startByte": 16553, + "endByte": 16563, + "startLine": 537, + "startColumn": 15, + "endLine": 537, + "endColumn": 25 + } + ] + } + ], + "weight": 1.0, + "context": "call" + }, + "interpretation": "Source-supported call is present across the community boundary; this split is not a missing-call finding." + }, + { + "repository": "jsoup", + "tool": "compass", + "caller": "isBlank", + "callee": "isWhitespace", + "callerCommunity": 17, + "calleeCommunity": 12, + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "line": 157, + "sourceLine": " if (!StringUtil.isWhitespace(string.codePointAt(i)))", + "storedEdge": { + "id": "sha256:b7685a82e4c39b4e15c0baf69a9b7c6b04494c82621884807f9a6932a4166869", + "key": "sha256:b7685a82e4c39b4e15c0baf69a9b7c6b04494c82621884807f9a6932a4166869", + "source": "sha256:7b70b1683af87dcfb443f8d622d324166f6a6a9d6d40e66b0f8baf1eea8ebbd7", + "target": "sha256:8b362a6fd1d3e3687e87d8010b0acf4af64a9bdf2c0cbd1ba51760278afb3227", + "kind": "calls", + "occurrenceRule": "universal-call-explicit-binding", + "relationshipSite": { + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startByte": 5238, + "endByte": 5250, + "startLine": 157, + "startColumn": 28, + "endLine": 157, + "endColumn": 40 + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.resolve.java.universal", + "confidence": "exact", + "rule": "universal-call-explicit-binding", + "anchors": [ + { + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "startByte": 5238, + "endByte": 5250, + "startLine": 157, + "startColumn": 28, + "endLine": 157, + "endColumn": 40 + } + ] + } + ], + "weight": 1.0, + "context": "call" + }, + "interpretation": "Source-supported call is present across the community boundary; this split is not a missing-call finding." + }, + { + "repository": "jsoup", + "tool": "graphify", + "caller": "parseBodyFragment", + "callee": "parseFragment", + "callerCommunity": 1, + "calleeCommunity": 39, + "file": "src/main/java/org/jsoup/parser/Parser.java", + "line": 341, + "sourceLine": " List nodeList = parseFragment(bodyHtml, body, baseUri);", + "storedEdge": { + "source": "src_main_java_org_jsoup_parser_parser_parser_parsebodyfragment", + "target": "src_main_java_org_jsoup_parser_parser_parser_parsefragment", + "relation": "calls", + "_origin": "ast", + "confidence": "EXTRACTED", + "confidence_score": 1.0, + "context": "call", + "source_file": "src/main/java/org/jsoup/parser/Parser.java", + "source_location": "L341", + "weight": 1.0 + }, + "interpretation": "Source-supported call is present across the community boundary; this split is not a missing-call finding." + }, + { + "repository": "jsoup", + "tool": "graphify", + "caller": "isBlank", + "callee": "isWhitespace", + "callerCommunity": 16, + "calleeCommunity": 48, + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "line": 157, + "sourceLine": " if (!StringUtil.isWhitespace(string.codePointAt(i)))", + "storedEdge": { + "source": "src_main_java_org_jsoup_internal_stringutil_stringutil_isblank", + "target": "src_main_java_org_jsoup_internal_stringutil_stringutil_iswhitespace", + "relation": "calls", + "_origin": "ast", + "confidence": "EXTRACTED", + "confidence_score": 1.0, + "context": "call", + "source_file": "src/main/java/org/jsoup/internal/StringUtil.java", + "source_location": "L157", + "weight": 1.0 + }, + "interpretation": "Source-supported call is present across the community boundary; this split is not a missing-call finding." + }, + { + "repository": "jsoup", + "tool": "graphify", + "caller": "clean", + "callee": "copySafeNodes", + "callerCommunity": 24, + "calleeCommunity": 129, + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "line": 66, + "sourceLine": " copySafeNodes(dirtyDocument.body(), clean.body());", + "storedEdge": { + "source": "src_main_java_org_jsoup_safety_cleaner_cleaner_clean", + "target": "src_main_java_org_jsoup_safety_cleaner_cleaner_copysafenodes", + "relation": "calls", + "_origin": "ast", + "confidence": "EXTRACTED", + "confidence_score": 1.0, + "context": "call", + "source_file": "src/main/java/org/jsoup/safety/Cleaner.java", + "source_location": "L66", + "weight": 1.0 + }, + "interpretation": "Source-supported call is present across the community boundary; this split is not a missing-call finding." + } + ], + "interpretation": [ + "All 30 source declarations resolve uniquely with community assignments in both graphs. Within-task co-location is 13/15 for Compass and 12/15 for Graphify. The two cross-file collaborator pairs are co-located on both sides; the other 13 within-task pairs share a source file.", + "Cross-task co-location is 18/60 for Compass and 12/60 for Graphify. These are granularity observations, not false-positive counts. Combining this with the collaborator count into a single accuracy score would impose an unreviewed architecture preference.", + "Click: Compass places term_len with help-table formatting and separates strip_ansi. The source explicitly shows measure_table using term_len. Graphify retains the term_len/strip_ansi pair together. Neither choice alone proves a bad cluster.", + "jsoup: Compass co-locates clean/copySafeNodes and parseBodyFragment/parseFragment; Graphify splits both pairs. Both tools split isBlank/isWhitespace. All five cross-community collaborator pairs across this panel retain the expected source-supported call edge.", + "Redux: Compass co-locates listener registration with action-creator binding; Graphify separates those selected tasks. The task labels do not prove separate ownership or a refactoring defect.", + "WalkDir: both tools put all six selected methods in one community. Their resource, deferred-output and symlink-loop mechanisms cooperate through the same iterator. The twelve cross-task pair co-locations are not twelve god-object defects.", + "Whole-community node and source-file counts provide context only. Tool node granularity and extraction coverage differ, and unselected community members have not all received semantic source review.", + "This study does not establish source precision for all graph edges, complete functional partition quality, source-based god-object labels, held-out generalization, or overall superiority. It motivates checking public navigation across community boundaries rather than forcing every collaborator into the same cluster." + ], + "nextActions": [ + "Compare a symmetric public community/neighbor workflow on the frozen collaborator pairs, preserving member-list truncation, ambiguity, exact identity availability, and complete response costs.", + "Review actual responsibilities of additional hub candidates with source-supported positive concerns and counterevidence; do not turn this task pair corpus into god-object labels.", + "Add independent semantic review and fresh repositories before any broad community quality or superiority claim." + ] +} diff --git a/benchmarks/agent_query/community_tasks.py b/benchmarks/agent_query/community_tasks.py new file mode 100644 index 000000000..44a8aad0e --- /dev/null +++ b/benchmarks/agent_query/community_tasks.py @@ -0,0 +1,213 @@ +"""Source-defined task co-location in captured native community assignments. + +This developer audit never invents an agent answer or a god-object label. +Cross-task co-location describes granularity; it is not a correctness failure. +""" + +from __future__ import annotations + +import argparse +from collections import Counter, defaultdict +import hashlib +from itertools import combinations +import json +from pathlib import Path +import subprocess + +from benchmarks.agent_query.runner import _node_anchor + + +MAX_JSON_BYTES = 256 * 1024 * 1024 +MAX_SOURCE_BYTES = 4 * 1024 * 1024 +MAX_DECLARATIONS = 256 + + +def read_bounded(path: Path, limit: int) -> bytes: + if type(limit) is not int or not 0 <= limit <= MAX_JSON_BYTES: + raise ValueError("invalid input byte limit") + with path.open("rb") as stream: + data = stream.read(limit + 1) + if len(data) > limit: + raise ValueError(f"input exceeds {limit} bytes: {path}") + return data + + +def digest(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def community_id(node: dict, tool: str) -> int | None: + value = node.get("community") + if value is None: + return None + if tool == "compass": + if not isinstance(value, dict): + raise ValueError("Compass community must be an object") + value = value.get("id") + elif tool != "graphify": + raise ValueError(f"unsupported tool: {tool}") + if type(value) is not int or value < 0: + raise ValueError("community id must be a nonnegative integer") + return value + + +def audit_graph(graph: dict, tool: str, tasks: list[dict], limits: dict) -> dict: + if tool not in {"compass", "graphify"}: + raise ValueError(f"unsupported tool: {tool}") + if not isinstance(tasks, list) or not 1 <= len(tasks) <= 64: + raise ValueError("invalid task count") + task_ids = [task["id"] for task in tasks] + if any(not isinstance(identifier, str) or not identifier for identifier in task_ids) or len(set(task_ids)) != len(task_ids): + raise ValueError("task ids must be nonempty and unique") + if sum(len(task["declarations"]) for task in tasks) > MAX_DECLARATIONS: + raise ValueError("declaration limit exceeded") + for key, cap in [("maxNodes", 200000), ("maxEdges", 1000000)]: + if type(limits[key]) is not int or not 1 <= limits[key] <= cap: + raise ValueError("invalid graph record limit") + nodes = graph.get("nodes") + edges = graph.get("links") + if not isinstance(nodes, list) or not isinstance(edges, list): + raise ValueError("graph must contain node and link arrays") + if len(nodes) > limits["maxNodes"] or len(edges) > limits["maxEdges"]: + raise ValueError("graph record limit exceeded") + index: dict[tuple, set[str]] = defaultdict(set) + by_id = {} + sizes = Counter() + files: dict[int, set[str]] = defaultdict(set) + for node in nodes: + if not isinstance(node, dict): + raise ValueError("node must be an object") + identifier = node.get("id") + if not isinstance(identifier, str) or not identifier or identifier in by_id: + raise ValueError("node ids must be nonempty and unique") + by_id[identifier] = node + file, line, names = _node_anchor(node, tool) + if file is not None and line is not None: + for name in names: + index[file, line, name].add(identifier) + group = community_id(node, tool) + if group is not None: + sizes[group] += 1 + if file is not None: + files[group].add(file) + + declarations = [] + seen_ids = set() + seen_anchors = set() + for task in tasks: + for declaration in task["declarations"]: + identifier = declaration["id"] + key = (declaration["file"], declaration["startLine"], declaration["symbol"]) + if (not isinstance(identifier, str) or not identifier + or not isinstance(key[0], str) or not key[0] + or type(key[1]) is not int or key[1] <= 0 + or not isinstance(key[2], str) or not key[2]): + raise ValueError("invalid source declaration identity") + if identifier in seen_ids or key in seen_anchors: + raise ValueError("repeated source declaration in registration") + seen_ids.add(identifier) + seen_anchors.add(key) + matches = sorted(index.get(key, set())) + row = dict(id=identifier, task=task["id"], file=declaration["file"], + startLine=declaration["startLine"], symbol=declaration["symbol"], + matchedNodeIds=matches, status="missing", community=None) + if len(matches) > 1: + row["status"] = "ambiguous" + elif matches: + group = community_id(by_id[matches[0]], tool) + row["community"] = group + row["status"] = "unassigned" if group is None else "resolved" + if group is not None: + row["communityNodes"] = sizes[group] + row["communitySourceFiles"] = len(files[group]) + declarations.append(row) + declarations.sort(key=lambda row: row["id"]) + pairs = [] + summaries = defaultdict(Counter) + for left, right in combinations(declarations, 2): + kind = "within_task" if left["task"] == right["task"] else "cross_task" + file_kind = "same_file" if left["file"] == right["file"] else "cross_file" + outcome = "unresolved" + if left["status"] == right["status"] == "resolved": + outcome = "same_community" if left["community"] == right["community"] else "different_community" + pairs.append(dict(left=left["id"], right=right["id"], kind=kind, + fileKind=file_kind, outcome=outcome)) + for scope in [kind, kind + "/" + file_kind]: + summaries[scope][outcome] += 1 + return dict(nodes=len(nodes), assignedNodes=sum(sizes.values()), communities=len(sizes), + declarations=declarations, pairs=pairs, + summaries={key: {outcome: counts[outcome] for outcome in + ["same_community", "different_community", "unresolved"]} + for key, counts in sorted(summaries.items())}) + + +def verify_source(repository: dict, source: Path) -> None: + commit = subprocess.run(["git", "-C", str(source), "rev-parse", "HEAD"], + check=True, capture_output=True, text=True, timeout=10).stdout.strip() + if commit != repository["commit"]: + raise ValueError("source commit differs from registration") + root = source.resolve() + for task in repository["tasks"]: + for declaration in task["declarations"]: + relative = Path(declaration["file"]) + if relative.is_absolute() or ".." in relative.parts: + raise ValueError("source path must be repository-relative") + path = (root / relative).resolve() + path.relative_to(root) + data = read_bounded(path, MAX_SOURCE_BYTES) + if digest(data) != declaration["sourceFileSha256"]: + raise ValueError("source file digest differs from registration") + start, end = declaration["startLine"], declaration["witnessEndLine"] + lines = data.decode("utf-8").splitlines() + if type(start) is not int or type(end) is not int or not 1 <= start <= end <= len(lines): + raise ValueError("invalid source witness range") + if "\n".join(lines[start - 1:end]) != declaration["witness"]: + raise ValueError("source witness differs from registration") + + +def execute(registration_path: Path, run_path: Path, output: Path) -> None: + registration_bytes = read_bounded(registration_path, MAX_SOURCE_BYTES) + registration = json.loads(registration_bytes) + if registration.get("schema") != "compass.community-task-pairs/1": + raise ValueError("unsupported registration schema") + if not 1 <= len(registration["repositories"]) <= 32: + raise ValueError("invalid repository count") + run_bytes = read_bounded(run_path, MAX_JSON_BYTES) + if digest(run_bytes) != registration["sourceRunSha256"]: + raise ValueError("source run digest differs from registration") + run = json.loads(run_bytes) + sources = {row["repository"]: row for row in run["repositories"]} + report = dict(schema="compass.community-task-audit/1", + scope=registration["scope"], registrationSha256=digest(registration_bytes), + sourceRunSha256=digest(run_bytes), + auditorSha256=digest(read_bounded(Path(__file__), MAX_SOURCE_BYTES)), + anchorMatcherSha256=digest(read_bounded(Path(__file__).with_name("runner.py"), MAX_SOURCE_BYTES)), + repositories=[]) + bounds = registration["policy"]["bounds"] + for repository in registration["repositories"]: + previous = sources[repository["repository"]] + source = Path(previous["source"]) + verify_source(repository, source) + row = dict(repository=repository["repository"], commit=repository["commit"], tools={}) + for tool in ["compass", "graphify"]: + data = read_bounded(Path(previous[tool + "Graph"]), min(bounds["maxGraphBytes"], MAX_JSON_BYTES)) + graph_digest = digest(data) + if graph_digest != repository["graphSha256"][tool]: + raise ValueError("graph digest differs from registration") + result = audit_graph(json.loads(data), tool, repository["tasks"], bounds) + result["graphSha256"] = graph_digest + row["tools"][tool] = result + report["repositories"].append(row) + # Never replace an earlier diagnostic capture. + with output.open("x", encoding="utf-8") as stream: + json.dump(report, stream, indent=2) + stream.write("\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--registration", type=Path, required=True) + parser.add_argument("--run", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + arguments = parser.parse_args() + execute(arguments.registration, arguments.run, arguments.output) diff --git a/benchmarks/agent_query/tests/test_community_tasks.py b/benchmarks/agent_query/tests/test_community_tasks.py new file mode 100644 index 000000000..61ae22ac6 --- /dev/null +++ b/benchmarks/agent_query/tests/test_community_tasks.py @@ -0,0 +1,130 @@ +import copy +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.community_tasks import audit_graph, read_bounded + + +class CommunityTaskTests(unittest.TestCase): + limits = dict(maxNodes=100, maxEdges=100) + + def tasks(self): + return [dict(id="task-a", declarations=[ + dict(id="a", file="a.rs", startLine=10, symbol="alpha"), + dict(id="b", file="b.rs", startLine=20, symbol="beta"), + ]), dict(id="task-b", declarations=[ + dict(id="c", file="a.rs", startLine=30, symbol="gamma"), + ])] + + def graph(self, tool="compass"): + nodes = [] + for identifier, name, file, line, group in [ + ("a", "alpha", "a.rs", 10, 0), + ("b", "beta", "b.rs", 20, 0), + ("c", "gamma", "a.rs", 30, 1), + ]: + if tool == "compass": + nodes.append(dict(id=identifier, name=name, source=dict(file=file, startLine=line), community=dict(id=group))) + else: + nodes.append(dict(id=identifier, label=name + "()", source_file=file, + source_location=f"L{line}", community=group)) + return dict(nodes=nodes, links=[]) + + def test_zero_is_a_real_community_and_pair_denominators_are_separate(self): + for tool in ["compass", "graphify"]: + result = audit_graph(self.graph(tool), tool, self.tasks(), self.limits) + self.assertEqual(result["summaries"]["within_task"]["same_community"], 1) + self.assertEqual(result["summaries"]["cross_task"]["different_community"], 2) + self.assertEqual(result["summaries"]["within_task/cross_file"]["same_community"], 1) + self.assertEqual(result["declarations"][0]["communityNodes"], 2) + self.assertEqual(result["declarations"][0]["communitySourceFiles"], 2) + + def test_node_order_and_community_renumbering_do_not_change_pair_outcomes(self): + graph = self.graph() + first = audit_graph(graph, "compass", self.tasks(), self.limits) + graph["nodes"].reverse() + for node in graph["nodes"]: + node["community"]["id"] += 90 + second = audit_graph(graph, "compass", self.tasks(), self.limits) + self.assertEqual(first["pairs"], second["pairs"]) + self.assertEqual(first["summaries"], second["summaries"]) + + def test_missing_and_unassigned_nodes_are_not_separated_successes(self): + for mode in ["missing", "unassigned"]: + graph = self.graph() + if mode == "missing": + graph["nodes"].pop(1) + else: + graph["nodes"][1].pop("community") + result = audit_graph(graph, "compass", self.tasks(), self.limits) + self.assertEqual(result["declarations"][1]["status"], mode) + self.assertEqual(result["summaries"]["within_task"]["unresolved"], 1) + self.assertEqual(result["summaries"]["cross_task"]["unresolved"], 1) + + def test_duplicate_anchor_preserves_both_identities_as_ambiguous(self): + graph = self.graph() + duplicate = copy.deepcopy(graph["nodes"][0]) + duplicate["id"] = "a-second" + duplicate["community"]["id"] = 1 + graph["nodes"].append(duplicate) + result = audit_graph(graph, "compass", self.tasks(), self.limits) + self.assertEqual(result["declarations"][0]["status"], "ambiguous") + self.assertEqual(result["declarations"][0]["matchedNodeIds"], ["a", "a-second"]) + self.assertEqual(result["summaries"]["within_task"]["unresolved"], 1) + + def test_exact_start_and_name_are_required_not_container_extent_or_substring(self): + for change in [dict(name="alphabet"), dict(source=dict(file="a.rs", startLine=1, endLine=50))]: + graph = self.graph() + graph["nodes"][0].update(change) + result = audit_graph(graph, "compass", self.tasks(), self.limits) + self.assertEqual(result["declarations"][0]["status"], "missing") + + def test_boolean_and_negative_communities_fail_closed(self): + for tool in ["compass", "graphify"]: + for value in [False, -1, "0"]: + graph = self.graph(tool) + graph["nodes"][0]["community"] = dict(id=value) if tool == "compass" else value + with self.assertRaises(ValueError): + audit_graph(graph, tool, self.tasks(), self.limits) + + def test_duplicate_node_ids_and_repeated_source_judgments_fail(self): + graph = self.graph() + graph["nodes"].append(copy.deepcopy(graph["nodes"][0])) + with self.assertRaises(ValueError): + audit_graph(graph, "compass", self.tasks(), self.limits) + tasks = self.tasks() + tasks[1]["declarations"] = copy.deepcopy(tasks[0]["declarations"]) + with self.assertRaises(ValueError): + audit_graph(self.graph(), "compass", tasks, self.limits) + + def test_duplicate_task_ids_and_large_pair_sets_fail(self): + tasks = self.tasks() + tasks[1]["id"] = tasks[0]["id"] + with self.assertRaises(ValueError): + audit_graph(self.graph(), "compass", tasks, self.limits) + with self.assertRaises(ValueError): + audit_graph(self.graph(), "compass", [dict(id="a", declarations=[{}] * 257)], self.limits) + + def test_graph_and_file_bounds_are_errors_not_empty_evidence(self): + with self.assertRaises(ValueError): + audit_graph(self.graph(), "compass", self.tasks(), dict(maxNodes=2, maxEdges=100)) + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / "input.json" + path.write_bytes(b"abcde") + self.assertEqual(read_bounded(path, 5), b"abcde") + with self.assertRaises(ValueError): + read_bounded(path, 4) + for limit in [-1, False, 10**12]: + with self.assertRaises(ValueError): + read_bounded(path, limit) + + def test_boolean_source_line_is_not_an_integer_anchor(self): + tasks = self.tasks() + tasks[0]["declarations"][0]["startLine"] = True + with self.assertRaises(ValueError): + audit_graph(self.graph(), "compass", tasks, self.limits) + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index a81c5bf5f..c2f7683f1 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -1959,6 +1959,59 @@ is not counted as a completed baseline. Extraction was not rerun for these query-only changes. Full response precision, new source-follow-up coverage, held-out confirmation, god-object diagnosis and community quality remain open. +### Source-defined community task pairs + +Commit `234753eb` freezes +[30 exact declarations](../../benchmarks/agent_query/community_task_pairs_panel_a.json) +and their source mechanisms before this task-pair membership audit. Earlier +comparisons had already exposed parts of these graphs, so this is a development +diagnostic. The three task pairs per repository yield 15 within-task pairs and +60 cross-task pairs. Both tools use the unchanged captured native graphs. + +All 30 declarations resolve uniquely on both sides. The +[review](../../benchmarks/agent_query/community_task_pairs_review_panel_a.json) +records identities, memberships, source witnesses and whole-community sizes. + +| Repository | Compass collaborator pairs co-located | Graphify collaborator pairs co-located | Compass cross-task pairs co-located | Graphify cross-task pairs co-located | +| --- | ---: | ---: | ---: | ---: | +| Chi | 3/3 | 3/3 | 0/12 | 0/12 | +| Click | 2/3 | 3/3 | 2/12 | 0/12 | +| jsoup | 2/3 | 0/3 | 0/12 | 0/12 | +| Redux | 3/3 | 3/3 | 4/12 | 0/12 | +| WalkDir | 3/3 | 3/3 | 12/12 | 12/12 | +| Total | 13/15 | 12/15 | 18/60 | 12/60 | + +These columns describe different tradeoffs. They are not a combined accuracy +score, and cross-task co-location is not a false-positive count. Only two of +the 15 collaborator pairs cross source files; both tools co-locate those two. +The purposefully small task labels do not partition all source responsibilities. + +Source review of all five split collaborator pairs finds the expected direct +call edge in the corresponding graph: + +- Compass splits Click `term_len` from `strip_ansi`, while placing `term_len` + with help-table layout. The source shows `measure_table` using `term_len`, + so there is a concrete reason for that cross-task grouping. +- Both tools split jsoup `isBlank` from `isWhitespace`. +- Graphify also splits jsoup `clean` from `copySafeNodes`, and + `parseBodyFragment` from the selected three-argument `parseFragment`. + +The five boundaries are not missing-call findings. They motivate testing +navigation to collaborators outside a community. Likewise, both tools place +WalkDir's handle budgeting, deferred-directory output and symlink-loop methods +in one community. Those mechanisms share iterator state; this does not prove +excessive responsibility. Whole-community size comparisons also reflect +different extraction granularity, including fields, parameters and containers. + +The bounded auditor reproduces all 150 tool/pair outcomes after hardening input +validation. All **109 developer-harness tests** pass, including ten new tests +for exact identity, ambiguity, missing assignments, integer-zero communities, +bounds and deterministic pair outcomes. A separate direct recomputation checks +the captured IDs and pairs, but it is not an independent semantic reviewer. +No Rust code or clustering algorithm changed in this iteration. Public +community/navigation workflow costs, broader membership precision, positive +god-object evidence, independent review and fresh confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 7a6d6c97ced7763e3086aded00df24a7976942d6 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:33:06 -0700 Subject: [PATCH 48/97] test: freeze community-to-neighbor navigation protocol --- benchmarks/agent_query/COVERAGE_PLAN.md | 14 ++++++ .../community_navigation_panel_a.json | 50 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 benchmarks/agent_query/community_navigation_panel_a.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index bcaf2949b..c4595b7ea 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -213,3 +213,17 @@ complete top-N eligibility, and ranking correctness remain separate questions. - Existing fixes and development-suite leads do not satisfy the complete objective. God-object judgments and the additional community/ask surfaces still require measured evidence across the repository panel. + +### Community-to-neighbor workflow + +`community_navigation_panel_a.json` freezes a development workflow over all 15 +source-defined tasks. Prepare each starting community symmetrically, then choose +one calls-neighbor lookup solely from the returned member label matching the +seed file and terminal symbol. Retain duplicate rows and stop on multiple +distinct selectors. Score ambiguity, displayed adjacency and exact identity +separately; never use the expected neighbors to identify an ambiguous seed. +Fourteen pairs require a direct call; Chi's request-ID pair shares context state. +Both tools receive common external 60-second/1-MiB response bounds, with +Graphify's generous explicit token allowance disclosed. Preserve complete +transcripts and startup overhead. This is a capability diagnostic, not an +output-efficiency, source-precision or whole-architecture score. diff --git a/benchmarks/agent_query/community_navigation_panel_a.json b/benchmarks/agent_query/community_navigation_panel_a.json new file mode 100644 index 000000000..da72a21fb --- /dev/null +++ b/benchmarks/agent_query/community_navigation_panel_a.json @@ -0,0 +1,50 @@ +{ + "schema": "compass.community-navigation-policy/1", + "scope": "Development diagnostic on reused graphs and source-reviewed tasks; no held-out or overall-superiority claim. Frozen before this public community-to-neighbor capture.", + "registrationSha256": "71faed0f477ee97da98c8bbb86686fae09de450664e4e000d9b2404496b94b6d", + "startingInput": "For every task, prepare the community containing the first exact source declaration symmetrically from each stored graph. Preparation is not scored as retrieval. Missing, ambiguous or unassigned inputs remain unresolved.", + "selectorPolicy": "Call get_community. Parse complete text member rows as two spaces, displayed label, space, [source file]. Match the public seed file and exact terminal symbol using runner._terminal_symbol. Retain every matching row. If exactly one distinct displayed label exists, send that label to get_neighbors even if repeated identically; record repeated rows. Otherwise stop unresolved. Never consult a graph to choose or substitute a follow-up selector. This baseline does not consume future structured community schemas.", + "followup": { + "tool": "get_neighbors", + "relation_filter": "calls", + "maximumCallsPerTask": 1, + "disambiguationRetries": 0 + }, + "bounds": { + "requestTimeoutSeconds": 60, + "maxResponseBytes": 1048576, + "maxSessionBytes": 67108864, + "graphifyTokenBudget": 262144 + }, + "budgetInterpretation": "Generous full-enumeration capability arm. Common external time/byte bounds; Graphify explicit token_budget and Compass whole-result interface are unequal native controls. Preserve wire bytes including initialization/listing and all errors. No latency or token-efficiency superiority claim.", + "scoring": [ + "Separate successful transport/tool execution, selected label, explicit ambiguity, matching seed heading, and identity support. Graphs may be consulted only for preparation/scoring, never follow-up selection.", + "Seed identity support requires a successful expected heading whose displayed label maps to exactly the expected source declaration across the entire stored graph. Do not disambiguate using expected adjacency or degree.", + "Compare displayed direction/name counts against distinct neighbors on stored calls edges. This is projection consistency, not source precision, complete relation coverage, or occurrence recall.", + "For the 14 reviewed direct-call tasks, report a displayed outgoing collaborator calls row separately from exact collaborator identity support. Exact support also requires seed identity support and the displayed target label to be unique graph-wide and equal the reviewed target ID. Do not substitute a convenient overloaded declaration.", + "Chi RequestID/GetReqID share a context key and have no direct call requirement. Keep that task in the 15-task navigation denominator, exclude it from the 14-task direct-collaborator denominator.", + "Report community splits separately; different tool-specific split subsets are not equal denominators. Record original community size, matching-row count, output sizes, failures and all unresolved outcomes." + ], + "directCallTasks": [ + "chi-router-construction", + "chi-route-pattern-parameters", + "click-atomic-close", + "click-terminal-string-width", + "click-help-definition-layout", + "jsoup-safe-document-copy", + "jsoup-html-fragment-parse", + "jsoup-html-whitespace", + "redux-listener-registration", + "redux-action-shape-validation", + "redux-action-creator-binding", + "walkdir-directory-handle-budget", + "walkdir-deferred-directory-depth", + "walkdir-symlink-loop-detection" + ], + "limitations": [ + "Only 15 purposively selected development tasks across five pinned repositories.", + "Public get_community to one get_neighbors follow-up is one workflow; additional searches, IDs from other tools and disambiguation retries are unmeasured.", + "Graph-wide display-label uniqueness is a conservative identity audit, not information directly supplied in the successful text response.", + "No extra assertion precision, architecture defect, god-object diagnosis or agent-generated explanation is scored." + ] +} From a1b631d3d80e7bc0dbe51d2f0020a632b148fd5c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:39:51 -0700 Subject: [PATCH 49/97] test: audit public community-to-neighbor navigation failures --- benchmarks/agent_query/README.md | 32 ++ .../agent_query/community_navigation.py | 230 ++++++++ .../community_navigation_review_panel_a.json | 539 ++++++++++++++++++ .../tests/test_community_navigation.py | 87 +++ ...ode-graph-intelligence-audit-2026-09-26.md | 66 +++ 5 files changed, 954 insertions(+) create mode 100644 benchmarks/agent_query/community_navigation.py create mode 100644 benchmarks/agent_query/community_navigation_review_panel_a.json create mode 100644 benchmarks/agent_query/tests/test_community_navigation.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 0e1912b35..a4947c3d5 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -388,3 +388,35 @@ report supersedes that claim. Preserve original artifacts. Disconnected pairs include source files/modules as well as declarations. A search that reaches its depth or work bound has not proved global disconnection and must remain an incomplete answer to that question. + +### Community-to-neighbor development workflow + +`community_navigation_panel_a.json` freezes the one-follow-up label baseline +before capture in commit `7a6d6c97`. Run it on the same paired graph manifest: + +```bash +python3 -m benchmarks.agent_query.community_navigation \ + --policy benchmarks/agent_query/community_navigation_panel_a.json \ + --registration benchmarks/agent_query/community_task_pairs_panel_a.json \ + --run /path/to/paired-graph-run/run.json \ + --output /path/to/new-community-navigation-run \ + --compass /path/to/frozen/compass \ + --graphify-python /path/to/isolated-graphify-mcp-env/bin/python \ + --graphify-environment /path/to/graphify-mcp-environment.json +``` + +Starting community IDs are prepared symmetrically. Follow-up labels use only +the community text and public seed file/symbol. All matching rows are retained; +multiple distinct labels stop the workflow. Graphs are used for scoring only +after requests, never to substitute an expected follow-up ID. One request has +60 seconds and 1 MiB of external response allowance; sessions are bounded at +64 MiB. Graphify receives an explicit generous token budget; Compass exposes a +whole-result interface. Those native controls are not equal token budgets. + +The matching review records seed identity support on 5/15 Compass versus 9/15 +Graphify tasks, and direct collaborator support on 4/14 versus 8/14. These are +reused-repository development results for one particular workflow. Broader +matching, duplicate member labels and a missing WalkDir call explain distinct +Compass failures. Additional disambiguation calls and richer member handles +remain unmeasured. Report bytes alongside these unequal successful sets, not as +a matched-success efficiency claim. diff --git a/benchmarks/agent_query/community_navigation.py b/benchmarks/agent_query/community_navigation.py new file mode 100644 index 000000000..fd1120152 --- /dev/null +++ b/benchmarks/agent_query/community_navigation.py @@ -0,0 +1,230 @@ +"""Bounded community-to-neighbor development capture with output-only selectors. + +Scoring consults frozen graphs after requests. Displayed name matches are kept +separate from conservative exact-identity support and source-call requirements. +""" +from __future__ import annotations + +import argparse +from collections import Counter, defaultdict +import json +from pathlib import Path +import re +import shutil +import time + +from benchmarks.agent_query.community_tasks import ( + MAX_JSON_BYTES, MAX_SOURCE_BYTES, audit_graph, digest, read_bounded, verify_source, +) +from benchmarks.agent_query.mcp_audit import audit as audit_membership, label +from benchmarks.agent_query.mcp_compare import captured_repository, verify_environment +from benchmarks.agent_query.mcp_transport import StdioMcp +from benchmarks.agent_query.runner import _sha256_file, _terminal_symbol + + +MEMBER = re.compile(r"^ (.*) \[(.*)\]$", re.M) +NEIGHBOR = re.compile(r"^ (-->|<--) (.*?) \[([^\]]*)\] \[[^\]]*\](?: at=.*)?$", re.M) +ERRORS = (OSError, RuntimeError, ValueError, TimeoutError) + + +def select_label(text: str, seed: dict) -> dict: + """Only public task coordinates and returned text may select the follow-up.""" + rows = [dict(label=name, file=file) for name, file in MEMBER.findall(text) + if file == seed['file'] and _terminal_symbol(name) == seed['symbol']] + choices = sorted({row['label'] for row in rows}) + return dict(matchingRows=rows, distinctLabels=choices, + selector=choices[0] if len(choices) == 1 else None, + status='selected' if len(choices) == 1 else 'missing' if not choices else 'ambiguous') + + +def score_navigation(text: str, graph: dict, tool: str, seed: str, target: str, + succeeded: bool) -> dict: + """Never identify a duplicate label by its conveniently matching neighbors.""" + nodes = {node['id']: node for node in graph['nodes']} + names = defaultdict(list) + for node in nodes.values(): + names[label(node, tool)].append(node['id']) + seed_name, target_name = label(nodes[seed], tool), label(nodes[target], tool) + heading = text.splitlines()[0] if text else '' + header_matches = succeeded and heading == f'Neighbors of {seed_name}:' + actual = Counter((direction, name) for direction, name, relation in NEIGHBOR.findall(text) + if 'calls' in relation.lower()) + pairs = set() + direct_edges = [] + for edge in graph['links']: + relation = edge.get('kind' if tool == 'compass' else 'relation', '') + if 'calls' not in relation.lower(): + continue + a, b = edge['source'], edge['target'] + if a == seed: + pairs.add(('-->', b)) + if b == target: + direct_edges.append(edge) + if b == seed: + pairs.add(('<--', a)) + expected = Counter((direction, label(nodes[identifier], tool)) for direction, identifier in pairs) + identity = header_matches and names[seed_name] == [seed] + displayed = header_matches and actual['-->', target_name] > 0 + return dict(seedHeadingMatches=header_matches, + ambiguityReported=bool(re.search(r'\bambiguous\b', text, re.I)), + seedLabelCandidates=sorted(names[seed_name]), seedIdentitySupported=identity, + expectedDisplayedNeighbors=sum(expected.values()), returnedDisplayedNeighbors=sum(actual.values()), + displayedAdjacencyMatches=header_matches and expected == actual, + missingDisplayedNeighbors=list((expected-actual).elements()), + extraDisplayedNeighbors=list((actual-expected).elements()), + collaboratorDisplayed=displayed, + collaboratorLabelCandidates=sorted(names[target_name]), + collaboratorIdentitySupported=displayed and identity and names[target_name] == [target], + graphDirectCallRecords=len(direct_edges)) + + +def call(session, method, arguments): + started = time.monotonic() + row = dict(method=method, arguments=arguments, executionSucceeded=False) + try: + packet = session.send('tools/call', dict(name=method, arguments=arguments)) + result = packet.get('result', {}) + text = '\n'.join(item['text'] for item in result.get('content', []) if item.get('type') == 'text') + raw = read_bounded(session.directory / f'{packet["id"]:02}.response.jsonl', 1048576) + if packet not in [json.loads(line) for line in raw.splitlines() if line]: + raise ValueError('response differs from saved transcript') + row.update(response=packet, text=text, textBytes=len(text.encode()), wireResponseBytes=len(raw), + executionSucceeded='error' not in packet and not result.get('isError', False)) + except ERRORS as error: + row['captureError'] = str(error) + row['elapsedSeconds'] = time.monotonic() - started + return row + + +def execute(args): + policy_bytes = read_bounded(args.policy, MAX_SOURCE_BYTES) + registration_bytes = read_bounded(args.registration, MAX_SOURCE_BYTES) + run_bytes = read_bounded(args.run, MAX_JSON_BYTES) + policy, registration, run = map(json.loads, [policy_bytes, registration_bytes, run_bytes]) + if policy.get('schema') != 'compass.community-navigation-policy/1': + raise ValueError('unsupported workflow policy') + if registration.get('schema') != 'compass.community-task-pairs/1': + raise ValueError('unsupported task registration') + if digest(registration_bytes) != policy['registrationSha256'] or digest(run_bytes) != registration['sourceRunSha256']: + raise ValueError('registered input hash differs') + if policy['bounds'] != dict(requestTimeoutSeconds=60, maxResponseBytes=1048576, + maxSessionBytes=67108864, graphifyTokenBudget=262144): + raise ValueError('unsupported workflow bounds') + repositories = registration['repositories'] + if not 1 <= len(repositories) <= 32 or len({r['repository'] for r in repositories}) != len(repositories): + raise ValueError('invalid repository list') + direct = policy['directCallTasks'] + all_tasks = [t['id'] for r in repositories for t in r['tasks']] + if len(set(all_tasks)) != len(all_tasks) or len(set(direct)) != len(direct) or not set(direct) <= set(all_tasks): + raise ValueError('invalid direct-call task list') + verify_environment(args) + args.output.mkdir(parents=True, exist_ok=False) + report = dict(schema='compass.community-navigation-capture/1', complete=False, + policySha256=digest(policy_bytes), registrationSha256=digest(registration_bytes), + sourceRunSha256=digest(run_bytes), sourceRun=str(args.run.resolve()), + graphifyEnvironmentSha256=_sha256_file(args.graphify_environment), + servers={}, supportFiles={}, sessions=[], results=[]) + for tool, path in [('compass', args.compass), ('graphify', args.graphify_python)]: + report['servers'][tool] = dict(path=str(path), sha256=_sha256_file(path)) + for path in [args.policy, args.registration, args.graphify_environment, *[ + Path(__file__).with_name(name) for name in ['community_navigation.py', 'community_tasks.py', + 'mcp_transport.py', 'mcp_compare.py', 'mcp_audit.py', 'runner.py']]]: + shutil.copy2(path, args.output/path.name) + report['supportFiles'][path.name] = _sha256_file(path) + def save(): + (args.output/'capture.json').write_text(json.dumps(report, indent=2)+'\n') + save() + for repository in repositories: + name = repository['repository'] + previous = captured_repository(run, name) + root = Path(previous['source']) + verify_source(repository, root) + for tool in ['compass', 'graphify']: + path = Path(previous[tool+'Graph']) + data = read_bounded(path, registration['policy']['bounds']['maxGraphBytes']) + graph_hash = digest(data) + if graph_hash != repository['graphSha256'][tool]: + raise ValueError('registered graph hash differs') + graph = json.loads(data) + prepared = audit_graph(graph, tool, repository['tasks'], registration['policy']['bounds']) + declarations = {d['id']: d for d in prepared['declarations']} + argv = [str(args.compass), 'serve'] if tool == 'compass' else [str(args.graphify_python), '-m', 'graphify.serve'] + argv += ['--graph', str(path), '--transport', 'stdio'] + budget = {'token_budget':262144} if tool == 'graphify' else {} + directory = args.output/'raw'/name/tool + session_row = dict(repository=name, tool=tool, argv=argv) + failure = None + started = time.monotonic() + try: + with StdioMcp(argv, root, directory, timeout=60, max_bytes=1048576) as session: + try: + session.initialize() + listing = session.send('tools/list', {}) + advertised = {t['name'] for t in listing.get('result', {}).get('tools', [])} + if not {'get_community', 'get_neighbors'} <= advertised: + raise ValueError('required public tools unavailable') + except ERRORS as error: + failure = str(error) + session_row['startupSeconds'] = time.monotonic() - started + for task in repository['tasks']: + if len(task['declarations']) != 2: + raise ValueError('workflow requires paired declarations') + seed, target = [declarations[d['id']] for d in task['declarations']] + row = dict(repository=name, tool=tool, task=task['id'], seed=seed, target=target, + graphSha256=graph_hash, directCallRequired=task['id'] in direct) + if failure is not None: + row['captureError'] = 'connection unavailable: '+failure + elif seed['status'] != 'resolved' or target['status'] != 'resolved': + row['inputUnresolved'] = True + else: + row['splitCommunity'] = seed['community'] != target['community'] + community = call(session, 'get_community', dict(community_id=seed['community'], **budget)) + row['communityCall'] = community + if 'captureError' in community: + failure = community['captureError'] + elif community['executionSucceeded']: + # The selector function has no graph access. + selection = select_label(community['text'], task['declarations'][0]) + row['selection'] = selection + if selection['selector'] is not None: + neighbors = call(session, 'get_neighbors', dict(label=selection['selector'], relation_filter='calls', **budget)) + row['neighborCall'] = neighbors + if 'captureError' in neighbors: + failure = neighbors['captureError'] + row['audit'] = score_navigation(neighbors.get('text', ''), graph, tool, + seed['matchedNodeIds'][0], target['matchedNodeIds'][0], neighbors['executionSucceeded']) + # Scoring is after requests, never selector preparation. + row['communityAudit'] = audit_membership(dict(repository=name, tool=tool, + question='community', **community), graph) + report['results'].append(row) + save() + print(name, tool, task['id'], row.get('selection', {}).get('status'), + row.get('audit', {}).get('seedIdentitySupported', False), + row.get('audit', {}).get('collaboratorIdentitySupported', False), flush=True) + except OSError as error: + session_row['launchError'] = str(error) + completed = {r['task'] for r in report['results'] if r['repository'] == name and r['tool'] == tool} + for task in repository['tasks']: + if task['id'] not in completed: + report['results'].append(dict(repository=name, tool=tool, task=task['id'], + directCallRequired=task['id'] in direct, captureError=str(error))) + session_row.update(elapsedSeconds=time.monotonic()-started, + wireFiles={p.name:p.stat().st_size for p in sorted(directory.glob('*')) if p.is_file()}) + report['sessions'].append(session_row) + save() + if _sha256_file(path) != graph_hash: + raise ValueError('graph changed during workflow') + verify_source(repository, root) + verify_environment(args) + for server in report['servers'].values(): + if _sha256_file(Path(server['path'])) != server['sha256']: + raise ValueError('server executable changed') + report['complete'] = True + save() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + for name in ['policy', 'registration', 'run', 'output', 'compass', 'graphify-python', 'graphify-environment']: + parser.add_argument('--'+name, type=Path, required=True) + execute(parser.parse_args()) diff --git a/benchmarks/agent_query/community_navigation_review_panel_a.json b/benchmarks/agent_query/community_navigation_review_panel_a.json new file mode 100644 index 000000000..38d45e5ad --- /dev/null +++ b/benchmarks/agent_query/community_navigation_review_panel_a.json @@ -0,0 +1,539 @@ +{ + "schema": "compass.community-navigation-review/1", + "scope": "Frozen one-follow-up public MCP development workflow on 15 source-defined tasks, five reused repositories. Not a general navigation, source-precision or superiority score.", + "policyCommit": "7a6d6c97", + "capture": "community-navigation-01/capture.json", + "captureSha256": "6c0da7060b53cb17ded2ca607563ae3a8723d76525897e709ffb4451f2e4bf2b", + "verificationSha256": "312c1a749409bd6983e0f0302f04ba450c2c4fb73e9c0f87c8f75726f73af7e5", + "summary": { + "compass": { + "tasks": 15, + "selected": 13, + "seedIdentitySupported": 5, + "directCallTasks": 14, + "collaboratorIdentitySupported": 4, + "directCallGraphPairsPresent": 13, + "explicitNeighborAmbiguity": 8, + "membershipConsistent": 15, + "callTextBytes": 145715, + "callResponseWireBytes": 150769, + "sessionWireBytesIncludingRequestsAndStderr": 238216 + }, + "graphify": { + "tasks": 15, + "selected": 15, + "seedIdentitySupported": 9, + "directCallTasks": 14, + "collaboratorIdentitySupported": 8, + "directCallGraphPairsPresent": 14, + "explicitNeighborAmbiguity": 6, + "membershipConsistent": 15, + "callTextBytes": 51605, + "callResponseWireBytes": 55173, + "sessionWireBytesIncludingRequestsAndStderr": 94443 + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "task": "chi-router-construction", + "selectedLabel": "NewRouter()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of NewRouter():", + "communityMembers": 90 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "selectedLabel": "RequestID()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": false, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Ambiguous: 'RequestID()' matches 5 nodes. Retry with an exact node ID.", + "communityMembers": 44 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-route-pattern-parameters", + "selectedLabel": "patParamKeys()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of patParamKeys():", + "communityMembers": 41 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-router-construction", + "selectedLabel": "NewRouter()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of NewRouter():", + "communityMembers": 108 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-request-identity", + "selectedLabel": "RequestID()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": false, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Neighbors of RequestID():", + "communityMembers": 58 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-route-pattern-parameters", + "selectedLabel": "patParamKeys()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of patParamKeys():", + "communityMembers": 42 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-atomic-close", + "selectedLabel": ".__exit__()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.__exit__()' matches 8 nodes. Retry with an exact node ID.", + "communityMembers": 203 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "selectedLabel": "term_len()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: 'term_len()' matches 2 nodes. Retry with an exact node ID.", + "communityMembers": 95 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-help-definition-layout", + "selectedLabel": ".write_dl()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .write_dl():", + "communityMembers": 95 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-atomic-close", + "selectedLabel": ".__exit__()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.__exit__()' matches 6 nodes in different files.", + "communityMembers": 9 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-terminal-string-width", + "selectedLabel": "term_len()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of term_len():", + "communityMembers": 24 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-help-definition-layout", + "selectedLabel": ".write_dl()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .write_dl():", + "communityMembers": 9 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-safe-document-copy", + "selectedLabel": ".clean()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.clean()' matches 18 nodes. Retry with an exact node ID.", + "communityMembers": 159 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-fragment-parse", + "selectedLabel": ".parseBodyFragment()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.parseBodyFragment()' matches 3 nodes. Retry with an exact node ID.", + "communityMembers": 815 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-whitespace", + "selectedLabel": ".isBlank()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.isBlank()' matches 4 nodes. Retry with an exact node ID.", + "communityMembers": 143 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-safe-document-copy", + "selectedLabel": ".clean()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.clean()' matches 2 nodes in different files.", + "communityMembers": 56 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-fragment-parse", + "selectedLabel": ".parseBodyFragment()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.parsebodyfragment()' matches 2 nodes in different files.", + "communityMembers": 232 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-whitespace", + "selectedLabel": ".isBlank()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.isblank()' matches 3 nodes in different files.", + "communityMembers": 76 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-listener-registration", + "selectedLabel": null, + "selectionStatus": "ambiguous", + "matchingRows": 3, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 2, + "heading": "", + "communityMembers": 55 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-shape-validation", + "selectedLabel": null, + "selectionStatus": "ambiguous", + "matchingRows": 2, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "", + "communityMembers": 252 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-creator-binding", + "selectedLabel": "bindActionCreators()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 2, + "heading": "Ambiguous: 'bindActionCreators()' matches 13 nodes. Retry with an exact node ID.", + "communityMembers": 55 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-listener-registration", + "selectedLabel": "subscribe()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: 'subscribe()' matches 2 nodes in different files.", + "communityMembers": 15 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-shape-validation", + "selectedLabel": "isAction()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: 'isaction()' matches 2 nodes in different files.", + "communityMembers": 8 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-creator-binding", + "selectedLabel": "bindActionCreators()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of bindActionCreators():", + "communityMembers": 14 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-directory-handle-budget", + "selectedLabel": ".push()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Neighbors of .push():", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-deferred-directory-depth", + "selectedLabel": ".get_deferred_dir()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .get_deferred_dir():", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "selectedLabel": ".follow()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.follow()' matches 21 nodes. Retry with an exact node ID.", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-directory-handle-budget", + "selectedLabel": ".push()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .push():", + "communityMembers": 33 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-deferred-directory-depth", + "selectedLabel": ".get_deferred_dir()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .get_deferred_dir():", + "communityMembers": 33 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-symlink-loop-detection", + "selectedLabel": ".follow()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .follow():", + "communityMembers": 33 + } + ], + "diagnostics": [ + { + "finding": "Unique displayed seed names can still produce broader matcher ambiguity in Compass.", + "tasks": [ + "chi-request-identity", + "click-terminal-string-width", + "walkdir-symlink-loop-detection" + ], + "evidence": "The successful community row uniquely names RequestID(), term_len(), or .follow() graph-wide. Neighbor lookup returns 5, 2 or 21 candidate matches respectively, including NextRequestID(), test_term_len(), or .follow_links(). find_node in compass-query score.rs appends exact, prefix and substring matches; get_neighbors treats the whole returned list as ambiguous. These are not missing source declarations." + }, + { + "finding": "Community display rows do not uniquely select two Compass Redux declaration inputs.", + "tasks": [ + "redux-listener-registration", + "redux-action-shape-validation" + ], + "evidence": "The same source file has three terminal-symbol subscribe matches and two isAction matches with distinct displayed labels. The frozen policy stops without substituting an expected graph ID or selecting a convenient row. Source declaration start lines are not present in the member text." + }, + { + "finding": "Both tools correctly report several genuine name collisions; one extra disambiguation call was outside this arm.", + "evidence": "Click __exit__ and all three selected jsoup seeds are ambiguous in both products. Graphify also reports subscribe and isAction ambiguity; Compass reports bindActionCreators overload/name ambiguity. Responses often supply candidate IDs; Graphify also suggests path::symbol. No additional calls were allowed, so these results do not show that a longer public workflow is impossible." + }, + { + "finding": "Compass lacks the reviewed WalkDir push-to-close edge in this frozen graph.", + "sourceFile": "src/lib.rs", + "line": 906, + "witness": "self.stack_list[self.oldest_opened].close();", + "evidence": "The exact push and DirList.close declarations resolve uniquely in both graphs. Compass has zero calls edges for that pair and its successful public neighbor response omits close; Graphify stores and displays the source-supported pair. This is a graph resolution/extraction gap distinct from member-handle selection. It is not diagnosed as a clustering defect." + }, + { + "finding": "No split collaborator is reached in this one-follow-up arm.", + "evidence": "Compass has two split pairs and Graphify three; all corresponding neighbor responses are ambiguous. The prior source-backed audit found the required call edges in all five cases. These unequal subsets are descriptive, not a separate head-to-head denominator." + } + ], + "verification": { + "calls": "All 58 executed public tool calls succeeded at the protocol/tool level; 30 community lists matched their stored membership. Two Compass follow-ups were not issued because selector selection was ambiguous. No timeout, cap failure or connection failure occurred.", + "tests": "119 developer-harness tests pass, including ten new selection/identity/direction/multiplicity regressions.", + "recomputation": "Separate same-agent script verifies saved request/response transcripts, support-file and graph hashes, seed identity and target counts. No independent semantic reviewer.", + "rust": "Not run: this iteration changes only developer evaluation and documentation, with no Rust/product changes." + }, + "limitations": [ + "Graph-wide name uniqueness is a conservative identity check; the text itself does not expose all identities.", + "All 15 inputs require prepared starting communities; starting community discovery is not scored.", + "Source-call requirements are 14 pairs; RequestID/GetReqID share context rather than a direct call. Distinct-neighbor results do not measure parallel call occurrence recall.", + "Generous full enumeration uses common external limits and unequal native token controls; text/wire byte totals include different community sizes and differing success counts, so are not equal-success efficiency evidence.", + "No clustering tuning, god-object diagnosis, comprehensive assertion precision, agent-generated explanation, held-out confirmation or overall winner is established." + ], + "serverExecutableSha256": { + "compass": "0bd42c8b4b5e88622747ec80920daa36a522ff326355fb35adea3b55f5e9fe3f", + "graphify": "bc9a27bd52c265e86fcd73ef1d4165ec8a47695cffb7624cb0ee618dca83e6c0" + } +} diff --git a/benchmarks/agent_query/tests/test_community_navigation.py b/benchmarks/agent_query/tests/test_community_navigation.py new file mode 100644 index 000000000..28a743734 --- /dev/null +++ b/benchmarks/agent_query/tests/test_community_navigation.py @@ -0,0 +1,87 @@ +import unittest + +from benchmarks.agent_query.community_navigation import score_navigation, select_label + + +class CommunityNavigationTests(unittest.TestCase): + seed = dict(file='a.rs', symbol='run', startLine=10) + + def graph(self): + return dict(nodes=[dict(id='a', name='Runner.run()', label='Runner.run()'), + dict(id='b', name='finish()', label='finish()')], + links=[dict(source='a', target='b', kind='calls', relation='calls')]) + + def test_selector_uses_exact_file_and_terminal_symbol(self): + text = 'Community 0 (4 nodes):\n Runner.run() [a.rs]\n run_more [a.rs]\n run [b.rs]\n Runner [a.rs]' + result = select_label(text, self.seed) + self.assertEqual(result['selector'], 'Runner.run()') + self.assertEqual(len(result['matchingRows']), 1) + + def test_identical_rows_are_retained_without_inventing_an_id(self): + result = select_label(' run [a.rs]\n run [a.rs]', self.seed) + self.assertEqual(result['selector'], 'run') + self.assertEqual(len(result['matchingRows']), 2) + + def test_distinct_labels_are_ambiguous_not_first_match(self): + result = select_label(' A.run() [a.rs]\n B.run() [a.rs]', self.seed) + self.assertIsNone(result['selector']) + self.assertEqual(result['status'], 'ambiguous') + + def test_missing_or_incomplete_member_rows_do_not_match(self): + for text in ['', ' run [a.rs', ' run [b.rs]', ' runner [a.rs]']: + self.assertEqual(select_label(text, self.seed)['status'], 'missing') + + def test_unique_seed_and_target_are_supported_for_both_tools(self): + for tool in ['compass', 'graphify']: + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [EXTRACTED] at=a.rs:L12', self.graph(), tool, 'a', 'b', True) + self.assertTrue(result['seedIdentitySupported']) + self.assertTrue(result['collaboratorIdentitySupported']) + self.assertTrue(result['displayedAdjacencyMatches']) + + def test_matching_adjacency_does_not_resolve_duplicate_seed(self): + graph = self.graph() + graph['nodes'].append(dict(id='duplicate', name='Runner.run()')) + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [exact]', graph, 'compass', 'a', 'b', True) + self.assertTrue(result['displayedAdjacencyMatches']) + self.assertTrue(result['collaboratorDisplayed']) + self.assertFalse(result['seedIdentitySupported']) + self.assertFalse(result['collaboratorIdentitySupported']) + + def test_duplicate_target_label_is_not_identity_support(self): + graph = self.graph() + graph['nodes'].append(dict(id='other', name='finish()')) + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [exact]', graph, 'compass', 'a', 'b', True) + self.assertTrue(result['seedIdentitySupported']) + self.assertTrue(result['collaboratorDisplayed']) + self.assertFalse(result['collaboratorIdentitySupported']) + + def test_failure_wrong_direction_or_relation_cannot_reach_collaborator(self): + for text, success in [ + ('Neighbors of Runner.run():\n --> finish() [calls] [exact]', False), + ('Neighbors of Runner.run():\n <-- finish() [calls] [exact]', True), + ('Neighbors of Runner.run():\n --> finish() [contains] [exact]', True), + ('Ambiguous: run matches 2 nodes.', True), + ]: + result = score_navigation(text, self.graph(), 'compass', 'a', 'b', success) + self.assertFalse(result['collaboratorIdentitySupported']) + self.assertFalse(result['collaboratorDisplayed']) + + def test_parallel_calls_are_distinct_neighbors_not_occurrence_recall(self): + graph = self.graph() + graph['links'] *= 2 + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [exact]', graph, 'compass', 'a', 'b', True) + self.assertEqual(result['graphDirectCallRecords'], 2) + self.assertEqual(result['expectedDisplayedNeighbors'], 1) + self.assertTrue(result['displayedAdjacencyMatches']) + + def test_duplicate_neighbor_names_preserve_multiplicity_of_identities(self): + graph = self.graph() + graph['nodes'].append(dict(id='other', name='finish()')) + graph['links'].append(dict(source='a', target='other', kind='calls')) + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [exact]', graph, 'compass', 'a', 'b', True) + self.assertEqual(result['expectedDisplayedNeighbors'], 2) + self.assertFalse(result['displayedAdjacencyMatches']) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index c2f7683f1..188c8f609 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2012,6 +2012,72 @@ No Rust code or clustering algorithm changed in this iteration. Public community/navigation workflow costs, broader membership precision, positive god-object evidence, independent review and fresh confirmation remain open. +### Public community-to-neighbor workflow + +Commit `7a6d6c97` freezes the +[one-follow-up protocol](../../benchmarks/agent_query/community_navigation_panel_a.json) +before capture. Every task starts at the community containing its first +source-defined declaration, prepared symmetrically from each tool's graph. +Starting-community discovery is not scored. The neighbor selector comes only +from returned member text matching the public seed file and terminal symbol. +Distinct matching labels remain unresolved; no expected ID is substituted. + +The [complete review](../../benchmarks/agent_query/community_navigation_review_panel_a.json) +records all 30 tool/task outcomes and the source-backed failure distinctions. + +| Measure | Compass | Graphify | +| --- | ---: | ---: | +| Community lists matching stored membership | 15/15 | 15/15 | +| Unambiguous follow-up label selected | 13/15 | 15/15 | +| Seed identity supported after one lookup | 5/15 | 9/15 | +| Direct collaborator identity supported | 4/14 | 8/14 | +| Neighbor calls reporting ambiguity | 8 | 6 | +| Required direct-call pairs present in graph | 13/14 | 14/14 | + +All 58 executed tool calls succeeded at the protocol/tool level. Two Compass +lookups were skipped because multiple distinct member labels matched. Successful +seed responses matched their stored displayed adjacency. That consistency does +not establish the precision of every returned edge. Chi's request-ID pair shares +context state and is excluded from the direct-call denominator. + +The failures expose separate improvement opportunities: + +- Compass's `get_neighbors` consumes the full exact/prefix/substring candidate + list from `find_node`. Unique displayed names `RequestID()`, `term_len()` and + `.follow()` still produce ambiguity with broader matches such as + `NextRequestID()`, `test_term_len()` and `.follow_links()`. +- Compass's Redux communities contain several member labels for the same seed + file and terminal symbol. Without declaration lines or IDs in the member + text, this policy cannot select the intended declaration. +- Click's `__exit__` and the selected jsoup names collide in both tools. Their + ambiguity responses often expose candidate IDs, and Graphify suggests + `path::symbol`. A longer disambiguation workflow remains a valid unmeasured + alternative; these failures do not prove navigation is impossible. +- Compass's successful WalkDir `push` response omits the reviewed call to + `DirList.close` at `src/lib.rs:906`. Both declarations are uniquely present, + but the captured Compass graph lacks that edge. Graphify stores and displays + it. This graph gap is separate from community grouping or selector ambiguity. + +Neither tool reaches its split collaborator pairs under this policy: Compass +has two such pairs, Graphify three. The earlier source review found the required +edges in all five cases. These different subsets are not equal denominators. + +The 60-second/1-MiB external response bounds are common, but native controls +are unequal: Graphify receives a generous explicit token budget and Compass +exposes whole results. Actual call text totals are 145,715 versus 51,605 bytes; +call response wire totals are 150,769 versus 55,173. Including initialization, +listing, requests and stderr, the session totals are 238,216 versus 94,443 bytes. +Different community sizes and success counts prevent a matched-success output +or latency efficiency claim. No cap or timeout occurred. + +All **119 developer-harness tests** pass, including ten new tests for selector +ambiguity, duplicate identities, direction and distinct-neighbor multiplicity. +A separate same-agent script checks all saved requests/responses, graph and +support-file hashes and identity/count summaries; it is not an independent +semantic reviewer. No Rust code changed or Rust tests ran in this iteration. +The workflow is development evidence on reused repositories, not a held-out +result, god-object diagnosis or overall superiority claim. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 8f5eb5e513c22e1645d57d22632db19dd8c82780 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:44:09 -0700 Subject: [PATCH 50/97] fix: prefer exact symbols for MCP neighbor navigation --- CHANGELOG.md | 4 + COMPATIBILITY.md | 13 +++- crates/compass-mcp/src/lib.rs | 139 +++++++++++++++++++++++++++++++++- docs/reference/outputs.md | 17 +++++ 4 files changed, 168 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67070872d..27b23f39a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Prefer exact IDs and symbol names for MCP neighbor navigation before broader + prefix/substring matches, while preserving genuine declaration ambiguity and + fuzzy fallback when no exact candidate exists. + - Preserve literal compound identifiers in natural discovery questions ahead of generic behavior matches, while retaining declaration collisions and uncertainty from bounded name lookups. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index f46e0cb55..4a8d4d64e 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -149,10 +149,15 @@ projections rebuild from their unchanged graph. MCP membership, statistics, and other traversal consumers can now observe those stored communities. Graph schemas and published historical graphs are unchanged. -MCP `get_neighbors` returns an explicit ambiguity list when multiple nodes -match, ordered by exact ID with at most 20 displayed candidates and an omission -count. Retry with an exact ID to choose a declaration. Exact IDs retain their -case. The tool's input schema and the MCP result envelope are unchanged. +MCP `get_neighbors` first resolves an exact ID or normalized symbol/qualified +name using the shared exact lookup, including its evidence-gated export-binding +handling. Prefix/substring fallback applies only when no exact candidate exists. +A unique exact `term_len()` therefore remains navigable when `test_term_len()` +exists. Multiple exact candidates remain ambiguous; ranking never chooses one. +The ambiguity list is ordered by exact ID with at most 20 displayed candidates +and an omission count. Retry with an exact ID to choose a declaration. Exact IDs +retain their case. The tool's input schema and the MCP result envelope are +unchanged. This corrects unnecessarily ambiguous lookups; no migration is needed. Relationship filters apply before repeated neighbors are grouped, so a stored call remains visible when a containment/reference edge precedes it. The tool continues to return distinct neighbors; typed call-query tools carry occurrence diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 34bd842b3..8551c210b 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -2225,7 +2225,12 @@ fn tool_get_neighbors( let filter = optional_string(arguments, "relation_filter") .unwrap_or_default() .to_lowercase(); - let matches = find_node(&context.graph, query); + let exact = find_exact_nodes(&context.graph, query); + let matches = if exact.is_empty() { + find_node(&context.graph, query) + } else { + exact + }; let Some(&index) = matches.first() else { return Ok(format!("No node matching '{query}' found.")); }; @@ -3235,6 +3240,138 @@ mod tests { Ok(()) } + #[test] + fn mcp_neighbors_prefer_exact_symbols_to_broader_names() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + for typed in [false, true] { + let path = temp.path().join(format!("exact-{typed}.json")); + let mut nodes = vec![ + json!({"id":"WidthID","label":"term_len()","qualifiedName":"Compat.term_len"}), + json!({"id":"width-test","label":"test_term_len()"}), + json!({"id":"request","label":"RequestID()"}), + json!({"id":"next-request","label":"NextRequestID()"}), + json!({"id":"follow","label":".follow()"}), + json!({"id":"follow-links","label":".follow_links()"}), + json!({"id":"callee","label":"Collaborator"}), + ]; + if typed { + for node in &mut nodes { + node["name"] = node["label"].clone(); + node.as_object_mut().ok_or("node")?.remove("label"); + node["kind"] = json!("function"); + node["source"] = json!({"file":"lib.rs","startLine":1}); + } + } + fs::write( + &path, + serde_json::to_vec(&json!({ + "directed":true,"nodes":nodes,"links":[ + {"source":"WidthID","target":"callee","relation":"calls"}, + {"source":"request","target":"callee","relation":"calls"}, + {"source":"follow","target":"callee","relation":"calls"} + ] + }))?, + )?; + let server = CompassMcp::new(&path); + for query in [ + "term_len()", + "term_len", + "Compat.term_len", + "WidthID", + " WidthID ", + "REQUESTID()", + ".follow()", + ] { + let output = server.invoke( + "get_neighbors", + json!({"label":query,"relation_filter":"calls"}) + .as_object() + .ok_or("args")? + .clone(), + ); + assert!( + output.starts_with("Neighbors of "), + "typed={typed} {query}: {output}" + ); + assert!(output.contains("--> Collaborator [calls]"), "{output}"); + } + } + Ok(()) + } + + #[test] + fn mcp_neighbors_preserve_all_exact_collisions_before_fuzzy_fallback() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + for reverse in [false, true] { + let path = temp.path().join(format!("collisions-{reverse}.json")); + let mut nodes = (0..22) + .map(|n| { + json!({"id":format!("exact-{n:02}"), + "label":"run()","source_file":format!("src/{n}.rs")}) + }) + .collect::>(); + nodes.push(json!({"id":"fuzzy","label":"runLater()"})); + if reverse { + nodes.reverse(); + } + fs::write( + &path, + serde_json::to_vec(&json!({"nodes":nodes,"links":[]}))?, + )?; + let server = CompassMcp::new(&path); + let output = server.invoke( + "get_neighbors", + json!({"label":"run"}).as_object().ok_or("args")?.clone(), + ); + assert!( + output.starts_with("Ambiguous: 'run' matches 22 nodes."), + "{output}" + ); + assert!( + output.contains("2 additional candidates omitted"), + "{output}" + ); + assert!(!output.contains("fuzzy") && !output.contains("Neighbors of")); + let candidates = output + .lines() + .filter(|line| line.starts_with(" ")) + .collect::>(); + assert_eq!(candidates.len(), 20); + for (n, line) in candidates.iter().enumerate() { + assert!(line.ends_with(&format!("id: exact-{n:02}")), "{line}"); + } + } + Ok(()) + } + + #[test] + fn mcp_neighbors_keep_unique_and_ambiguous_fuzzy_fallbacks() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("fallback.json"); + fs::write( + &path, + serde_json::to_vec(&json!({"nodes":[ + {"id":"a","label":"UniqueHandle()"},{"id":"b","label":"SharedAlpha()"}, + {"id":"c","label":"SharedBeta()"}],"links":[]}))?, + )?; + let server = CompassMcp::new(&path); + for (query, expected) in [ + ("Unique", "Neighbors of UniqueHandle():"), + ("Shared", "Ambiguous: 'Shared' matches 2 nodes."), + ("Missing", "No node matching 'Missing' found."), + ] { + let output = server.invoke( + "get_neighbors", + json!({"label":query}).as_object().ok_or("args")?.clone(), + ); + assert!(output.starts_with(expected), "{output}"); + } + Ok(()) + } + #[test] fn mcp_neighbors_require_unique_identity() -> Result<(), Box> { let temp = tempfile::tempdir()?; diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index f9b5a0a23..d8aebab9b 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -907,6 +907,23 @@ line fields; the result preserves it without inventing a line number. The existing 16 MiB structured-response bound applies and fails explicitly rather than silently dropping entries. Consumers must check the schema version. +### MCP neighbor lookup + +`get_neighbors.label` accepts an exact node ID, normalized symbol or qualified +name. Exact IDs preserve case and select that identity directly. Symbol lookup +uses the same exact lookup and evidence-gated export-binding handling as MCP +paths. A unique exact symbol takes precedence over broader names: `term_len()` +resolves independently of `test_term_len()`. If no exact candidate exists, the +legacy prefix/substring lookup remains available. + +Multiple exact candidates produce an ambiguity list instead of neighbors. +Candidates are sorted by exact ID, with at most 20 displayed and an explicit +omission count. Retry using an exact ID to choose a declaration. Multiple fuzzy +candidates are also ambiguous. The text result and input schema are unchanged. +A successful result reports distinct incoming/outgoing neighbors after applying +`relation_filter`; use typed call-query results for individual occurrences and +source sites. Displayed neighbor labels alone may still be ambiguous. + ### Agent Query View The focused query commands and MCP query tools also expose the strict, From 500a4565a803a9f07c03eb036aa8200944387ff3 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:46:26 -0700 Subject: [PATCH 51/97] test: align MCP text assertion with shared compact renderer --- crates/compass-mcp/tests/code_query_tools.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/compass-mcp/tests/code_query_tools.rs b/crates/compass-mcp/tests/code_query_tools.rs index 0eea869a8..4b6b494ab 100644 --- a/crates/compass-mcp/tests/code_query_tools.rs +++ b/crates/compass-mcp/tests/code_query_tools.rs @@ -651,7 +651,7 @@ async fn mcp_code_queries_publish_structured_content_and_protocol_errors() .iter() .find_map(|content| content.as_text().map(|text| text.text.clone())) .ok_or("missing MCP text content")?; - assert!(text.starts_with("RESULT\n")); + assert!(text.starts_with("RESULT "), "{text}"); assert!(text.contains("ANSWER\n")); let structured = response .structured_content @@ -662,6 +662,9 @@ async fn mcp_code_queries_publish_structured_content_and_protocol_errors() "compass.query.agent-view/1" ); assert_eq!(structured["result"]["schema"], "compass.query/1"); + let agent_view: compass_output::AgentQueryView = + serde_json::from_value(structured["agentView"].clone())?; + assert_eq!(text, compass_output::render_agent_query_text(&agent_view)?); assert!( client .call_tool(CallToolRequestParams::new("search_symbols")) From 6bc29e92043426d67d09d1bbb49167237c07c156 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:50:32 -0700 Subject: [PATCH 52/97] test: stabilize missing and extra neighbor audit ordering --- benchmarks/agent_query/community_navigation.py | 4 ++-- .../agent_query/tests/test_community_navigation.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/benchmarks/agent_query/community_navigation.py b/benchmarks/agent_query/community_navigation.py index fd1120152..4fa4e64eb 100644 --- a/benchmarks/agent_query/community_navigation.py +++ b/benchmarks/agent_query/community_navigation.py @@ -70,8 +70,8 @@ def score_navigation(text: str, graph: dict, tool: str, seed: str, target: str, seedLabelCandidates=sorted(names[seed_name]), seedIdentitySupported=identity, expectedDisplayedNeighbors=sum(expected.values()), returnedDisplayedNeighbors=sum(actual.values()), displayedAdjacencyMatches=header_matches and expected == actual, - missingDisplayedNeighbors=list((expected-actual).elements()), - extraDisplayedNeighbors=list((actual-expected).elements()), + missingDisplayedNeighbors=sorted((expected-actual).elements()), + extraDisplayedNeighbors=sorted((actual-expected).elements()), collaboratorDisplayed=displayed, collaboratorLabelCandidates=sorted(names[target_name]), collaboratorIdentitySupported=displayed and identity and names[target_name] == [target], diff --git a/benchmarks/agent_query/tests/test_community_navigation.py b/benchmarks/agent_query/tests/test_community_navigation.py index 28a743734..9a5fd6783 100644 --- a/benchmarks/agent_query/tests/test_community_navigation.py +++ b/benchmarks/agent_query/tests/test_community_navigation.py @@ -74,6 +74,18 @@ def test_parallel_calls_are_distinct_neighbors_not_occurrence_recall(self): self.assertEqual(result['expectedDisplayedNeighbors'], 1) self.assertTrue(result['displayedAdjacencyMatches']) + def test_mismatch_diagnostics_have_stable_sorted_order(self): + graph = self.graph() + graph['nodes'].extend([dict(id='x', name='Zulu()'), dict(id='y', name='Alpha()')]) + graph['links'].extend([dict(source='a', target='x', kind='calls'), + dict(source='a', target='y', kind='calls')]) + text = 'Neighbors of Runner.run():\n --> ZExtra() [calls] [exact]\n --> AExtra() [calls] [exact]' + result = score_navigation(text, graph, 'compass', 'a', 'b', True) + self.assertEqual(result['missingDisplayedNeighbors'], + sorted([('-->', 'finish()'), ('-->', 'Zulu()'), ('-->', 'Alpha()')])) + self.assertEqual(result['extraDisplayedNeighbors'], + [('-->', 'AExtra()'), ('-->', 'ZExtra()')]) + def test_duplicate_neighbor_names_preserve_multiplicity_of_identities(self): graph = self.graph() graph['nodes'].append(dict(id='other', name='finish()')) From 2054373cb63cbe93ecaa9fb1b58152950024db2b Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:53:22 -0700 Subject: [PATCH 53/97] test: record exact-first neighbor navigation gains and remaining gaps --- benchmarks/agent_query/COVERAGE_PLAN.md | 9 + benchmarks/agent_query/README.md | 9 + .../neighbor_exact_match_review_panel_a.json | 763 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 65 ++ 4 files changed, 846 insertions(+) create mode 100644 benchmarks/agent_query/neighbor_exact_match_review_panel_a.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index c4595b7ea..fa9dbf900 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -227,3 +227,12 @@ Both tools receive common external 60-second/1-MiB response bounds, with Graphify's generous explicit token allowance disclosed. Preserve complete transcripts and startup overhead. This is a capability diagnostic, not an output-efficiency, source-precision or whole-architecture score. + +The same frozen workflow is rerun after the exact-first MCP neighbor fix in +`8f5eb5e5`. `neighbor_exact_match_review_panel_a.json` records Compass seed +identity support improving from 5/15 to 8/15 and collaborator support from 4/14 +to 6/14. Graphify remains at 9/15 and 8/14. All graph hashes, selected labels +and community texts stay unchanged. Keep the two member-selection ambiguities, +five genuine neighbor ambiguities and missing WalkDir call visible. A later +workflow should use each tool's documented source-qualified or exact-ID handles; +this label-only arm is not a best-possible agent navigation score. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index a4947c3d5..5c568fde5 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -420,3 +420,12 @@ matching, duplicate member labels and a missing WalkDir call explain distinct Compass failures. Additional disambiguation calls and richer member handles remain unmeasured. Report bytes alongside these unequal successful sets, not as a matched-success efficiency claim. + +`neighbor_exact_match_review_panel_a.json` records the unchanged workflow after +MCP neighbor lookup began preferring exact matches. Compass improves to 8/15 +seed identities and 6/14 direct collaborator identities; Graphify stays at 9/15 +and 8/14. The ten graph hashes, follow-up labels, community texts and all Graphify +neighbor texts remain identical. Missing/extra neighbor diagnostic arrays now +sort deterministically; older captures need order normalization for those two +arrays only. Genuine collisions, two ambiguous community selectors and the +missing WalkDir call remain open. diff --git a/benchmarks/agent_query/neighbor_exact_match_review_panel_a.json b/benchmarks/agent_query/neighbor_exact_match_review_panel_a.json new file mode 100644 index 000000000..97280abd1 --- /dev/null +++ b/benchmarks/agent_query/neighbor_exact_match_review_panel_a.json @@ -0,0 +1,763 @@ +{ + "schema": "compass.neighbor-exact-match-review/1", + "scope": "Same frozen public community-to-neighbor development workflow before and after exact-first MCP lookup. No new task selection, graph rebuild, extra follow-up or source assertion oracle.", + "fixCommit": "8f5eb5e5", + "transportTestCorrectionCommit": "500a4565", + "auditorOrderingCommit": "6bc29e92", + "protocolCommit": "7a6d6c97", + "binarySourceCommit": "500a4565a803a9f07c03eb036aa8200944387ff3", + "binarySha256": "1174ab1535e4614e08b04a283a90fe33ee53e82517907ff85c323cacb6d9b75a", + "compiler": "rustc 1.97.1 (8bab26f4f 2026-07-14)", + "productVersion": "compass 0.3.30", + "artifacts": { + "before": "community-navigation-01", + "after": "neighbor-exact-navigation-02", + "initialAfter": "neighbor-exact-navigation-01", + "validation": "neighbor-exact-match-02", + "failingRegressionsAndInitialValidation": "neighbor-exact-match-01" + }, + "comparison": { + "beforeSha256": "6c0da7060b53cb17ded2ca607563ae3a8723d76525897e709ffb4451f2e4bf2b", + "afterSha256": "d4f686de90277dc17c70b4643ed4f06e3de9b98afa24c597ba4513bea4511999", + "unchanged": "All ten graph hashes, source declarations, follow-up selectors and community texts; all Graphify neighbor texts and audit outcomes (mismatch-list order canonicalized for the older auditor).", + "summary": { + "before": { + "compass": { + "tasks": 15, + "seedIdentitySupported": 5, + "directCallTasks": 14, + "collaboratorIdentitySupported": 4, + "explicitAmbiguities": 8, + "callTextBytes": 145715, + "callResponseWireBytes": 150769 + }, + "graphify": { + "tasks": 15, + "seedIdentitySupported": 9, + "directCallTasks": 14, + "collaboratorIdentitySupported": 8, + "explicitAmbiguities": 6, + "callTextBytes": 51605, + "callResponseWireBytes": 55173 + } + }, + "after": { + "compass": { + "tasks": 15, + "seedIdentitySupported": 8, + "directCallTasks": 14, + "collaboratorIdentitySupported": 6, + "explicitAmbiguities": 5, + "callTextBytes": 139718, + "callResponseWireBytes": 144736 + }, + "graphify": { + "tasks": 15, + "seedIdentitySupported": 9, + "directCallTasks": 14, + "collaboratorIdentitySupported": 8, + "explicitAmbiguities": 6, + "callTextBytes": 51605, + "callResponseWireBytes": 55173 + } + } + }, + "changes": [ + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "metric": "seedIdentitySupported", + "before": false, + "after": true + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "metric": "displayedAdjacencyMatches", + "before": false, + "after": true + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "metric": "ambiguityReported", + "before": true, + "after": false + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "metric": "seedIdentitySupported", + "before": false, + "after": true + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "metric": "collaboratorIdentitySupported", + "before": false, + "after": true + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "metric": "displayedAdjacencyMatches", + "before": false, + "after": true + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "metric": "ambiguityReported", + "before": true, + "after": false + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "metric": "seedIdentitySupported", + "before": false, + "after": true + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "metric": "collaboratorIdentitySupported", + "before": false, + "after": true + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "metric": "displayedAdjacencyMatches", + "before": false, + "after": true + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "metric": "ambiguityReported", + "before": true, + "after": false + } + ] + }, + "summary": { + "compass": { + "tasks": 15, + "selected": 13, + "seedIdentitySupported": 8, + "directCallTasks": 14, + "collaboratorIdentitySupported": 6, + "directCallGraphPairsPresent": 13, + "explicitNeighborAmbiguity": 5, + "membershipConsistent": 15, + "callTextBytes": 139718, + "callResponseWireBytes": 144736, + "sessionWireBytesIncludingRequestsAndStderr": 232183 + }, + "graphify": { + "tasks": 15, + "selected": 15, + "seedIdentitySupported": 9, + "directCallTasks": 14, + "collaboratorIdentitySupported": 8, + "directCallGraphPairsPresent": 14, + "explicitNeighborAmbiguity": 6, + "membershipConsistent": 15, + "callTextBytes": 51605, + "callResponseWireBytes": 55173, + "sessionWireBytesIncludingRequestsAndStderr": 94443 + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "task": "chi-router-construction", + "selectedLabel": "NewRouter()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of NewRouter():", + "communityMembers": 90 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "selectedLabel": "RequestID()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": false, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Neighbors of RequestID():", + "communityMembers": 44 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-route-pattern-parameters", + "selectedLabel": "patParamKeys()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of patParamKeys():", + "communityMembers": 41 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-router-construction", + "selectedLabel": "NewRouter()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of NewRouter():", + "communityMembers": 108 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-request-identity", + "selectedLabel": "RequestID()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": false, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Neighbors of RequestID():", + "communityMembers": 58 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-route-pattern-parameters", + "selectedLabel": "patParamKeys()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of patParamKeys():", + "communityMembers": 42 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-atomic-close", + "selectedLabel": ".__exit__()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.__exit__()' matches 7 nodes. Retry with an exact node ID.", + "communityMembers": 203 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "selectedLabel": "term_len()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of term_len():", + "communityMembers": 95 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-help-definition-layout", + "selectedLabel": ".write_dl()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .write_dl():", + "communityMembers": 95 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-atomic-close", + "selectedLabel": ".__exit__()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.__exit__()' matches 6 nodes in different files.", + "communityMembers": 9 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-terminal-string-width", + "selectedLabel": "term_len()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of term_len():", + "communityMembers": 24 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-help-definition-layout", + "selectedLabel": ".write_dl()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .write_dl():", + "communityMembers": 9 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-safe-document-copy", + "selectedLabel": ".clean()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.clean()' matches 4 nodes. Retry with an exact node ID.", + "communityMembers": 159 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-fragment-parse", + "selectedLabel": ".parseBodyFragment()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.parseBodyFragment()' matches 3 nodes. Retry with an exact node ID.", + "communityMembers": 815 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-whitespace", + "selectedLabel": ".isBlank()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.isBlank()' matches 3 nodes. Retry with an exact node ID.", + "communityMembers": 143 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-safe-document-copy", + "selectedLabel": ".clean()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.clean()' matches 2 nodes in different files.", + "communityMembers": 56 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-fragment-parse", + "selectedLabel": ".parseBodyFragment()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.parsebodyfragment()' matches 2 nodes in different files.", + "communityMembers": 232 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-whitespace", + "selectedLabel": ".isBlank()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": true, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: '.isblank()' matches 3 nodes in different files.", + "communityMembers": 76 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-listener-registration", + "selectedLabel": null, + "selectionStatus": "ambiguous", + "matchingRows": 3, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 2, + "heading": "", + "communityMembers": 55 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-shape-validation", + "selectedLabel": null, + "selectionStatus": "ambiguous", + "matchingRows": 2, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "", + "communityMembers": 252 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-creator-binding", + "selectedLabel": "bindActionCreators()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 2, + "heading": "Ambiguous: 'bindActionCreators()' matches 10 nodes. Retry with an exact node ID.", + "communityMembers": 55 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-listener-registration", + "selectedLabel": "subscribe()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: 'subscribe()' matches 2 nodes in different files.", + "communityMembers": 15 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-shape-validation", + "selectedLabel": "isAction()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": false, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 1, + "heading": "Ambiguous: 'isaction()' matches 2 nodes in different files.", + "communityMembers": 8 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-creator-binding", + "selectedLabel": "bindActionCreators()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of bindActionCreators():", + "communityMembers": 14 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-directory-handle-budget", + "selectedLabel": ".push()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": false, + "graphDirectCallRecords": 0, + "heading": "Neighbors of .push():", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-deferred-directory-depth", + "selectedLabel": ".get_deferred_dir()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .get_deferred_dir():", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "selectedLabel": ".follow()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .follow():", + "communityMembers": 116 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-directory-handle-budget", + "selectedLabel": ".push()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .push():", + "communityMembers": 33 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-deferred-directory-depth", + "selectedLabel": ".get_deferred_dir()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .get_deferred_dir():", + "communityMembers": 33 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-symlink-loop-detection", + "selectedLabel": ".follow()", + "selectionStatus": "selected", + "matchingRows": 1, + "splitCommunity": false, + "seedIdentitySupported": true, + "directCallRequired": true, + "collaboratorIdentitySupported": true, + "graphDirectCallRecords": 1, + "heading": "Neighbors of .follow():", + "communityMembers": 33 + } + ], + "implementation": { + "behavior": "get_neighbors uses find_exact_nodes first. Only an empty exact candidate set falls back to find_node. Exact IDs retain precedence and case; normalized symbols and qualified names use the shared exact lookup, including evidence-gated export binding handling. All genuine exact collisions remain ambiguous, with stable bounded candidate lists.", + "ownership": "MCP delegates to the existing compass-query exact matcher; the broad search helper and graph extraction/clustering are unchanged.", + "compatibility": "No input schema, response envelope, graph schema, version, release or migration change." + }, + "verification": { + "native": [ + { + "name": "fmt", + "command": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "elapsedSeconds": 3.720431457972154 + }, + { + "name": "mcp-tests", + "command": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--tests", + "--locked" + ], + "exitCode": 0, + "elapsedSeconds": 15.112442665966228 + }, + { + "name": "clippy", + "command": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "elapsedSeconds": 8.008247874910012 + }, + { + "name": "workspace-tests", + "command": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "elapsedSeconds": 56.75049358303659 + }, + { + "name": "product-tests", + "command": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "elapsedSeconds": 26.76331787498202 + }, + { + "name": "product-boundary", + "command": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "elapsedSeconds": 0.04264066705945879 + }, + { + "name": "build", + "command": [ + "cargo", + "build", + "-p", + "compass-cli", + "--bin", + "compass", + "--locked" + ], + "exitCode": 0, + "elapsedSeconds": 0.8669613749952987 + } + ], + "mcpTests": { + "passed": 58, + "note": "38 library tests overlap with the workspace count; 20 integration tests are additional." + }, + "workspaceLibBins": { + "passed": 1101, + "ignored": 2 + }, + "productTests": { + "passed": 9 + }, + "developerHarness": { + "passed": 120 + }, + "regressionProof": "Before the product fix, two new neighbor tests failed (unique exact name polluted by fuzzy match; collision count polluted by fuzzy match), while genuine identity ambiguity and fuzzy fallback tests passed. All four pass after the fix.", + "staleAssertion": "The first all-MCP run passed 38 library and five integration tests, then failed one code-query transport assertion expecting RESULT followed by a newline. The compact shared renderer changed in earlier commit a985ea91. The assertion now checks the compact RESULT prefix and equality between MCP text and the validated structured Agent View rendered by the shared renderer. The complete rerun passes.", + "auditorOrdering": "The initial before/after verifier rejected differing order in missing-neighbor diagnostic arrays for three Graphify jsoup cases. Requests, responses, graph hashes, and all verdicts were unchanged. Sort both missing and extra diagnostic arrays, retain the failing regression, then run all 120 harness tests and recapture. The final comparison canonicalizes only those older diagnostic-array orders; all Graphify texts and remaining audit fields agree.", + "sourceAndTranscripts": "Separate same-agent verification checks every saved request/response, support file and graph hash plus identity/count summaries. All ten graphs, source declarations, selected follow-up labels and community texts are unchanged. Graphify neighbor texts are byte-identical. No independent semantic reviewer.", + "gatesNotRun": "No extraction, language, viewer or JavaScript changes; full extraction qualification and JS gates were not rerun. Native MCP, workspace, product identity and boundary checks ran.", + "warnings": "Workspace test logs retain the existing compass-core unused_mut warning and a macOS linker unwind-table warning. Clippy for workspace libraries and binaries passed with warnings denied." + }, + "remaining": { + "memberSelection": "Two Compass Redux community lists still have distinct matching labels without exact declaration handles.", + "genuineNameAmbiguity": "Click __exit__, three jsoup seeds and Redux bindActionCreators still require disambiguation.", + "missingCall": "The frozen Compass WalkDir graph still lacks push -> DirList.close at src/lib.rs:906.", + "comparison": "Graphify remains ahead on this specific one-follow-up arm: 9/15 seeds and 8/14 collaborators versus Compass 8/15 and 6/14.", + "scope": "Additional public path-qualified/ID workflows, neighbor identity projections, complete assertion precision, source-defined god-object positives, broader architecture judgment and fresh held-out confirmation remain open." + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 188c8f609..84d10e226 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2078,6 +2078,71 @@ semantic reviewer. No Rust code changed or Rust tests ran in this iteration. The workflow is development evidence on reused repositories, not a held-out result, god-object diagnosis or overall superiority claim. +### Exact-first neighbor lookup correction + +Commit `8f5eb5e5` fixes the broad-match ambiguity demonstrated above. +`get_neighbors` delegates first to the existing `find_exact_nodes` query helper; +only an empty exact candidate set uses the broader lookup. Exact IDs retain +case and precedence. Genuine normalized-name collisions remain ambiguous and +keep the same bounded, stable candidate list. The implementation reuses the +shared evidence-gated export-binding behavior rather than adding a second +resolver. Graphs and clustering do not change. + +The [complete rerun review](../../benchmarks/agent_query/neighbor_exact_match_review_panel_a.json) +uses the same frozen 15 tasks and one-follow-up policy. All ten graph hashes, +source declarations, selected labels and community texts are unchanged. Every +Graphify neighbor response is byte-identical to the baseline. + +| Measure | Compass before | Compass after | Graphify both runs | +| --- | ---: | ---: | ---: | +| Seed identity supported | 5/15 | 8/15 | 9/15 | +| Reviewed direct collaborator supported | 4/14 | 6/14 | 8/14 | +| Neighbor responses reporting ambiguity | 8 | 5 | 6 | + +Chi `RequestID`, Click `term_len` and WalkDir `follow` now resolve their exact +seed. The latter two expose their reviewed direct collaborators; the request-ID +pair has no direct-call requirement. No task loses a previously supported seed +or collaborator. Graphify remains ahead in this specific arm. + +Two Compass Redux member selectors still have multiple distinct labels. Click's +`__exit__`, the three jsoup seeds and Redux's `bindActionCreators` still produce +genuine neighbor ambiguity. WalkDir's `push -> DirList.close` edge remains +missing in the frozen Compass graph. The next navigation arm should use each +tool's documented source-qualified or exact-ID handles and examine whether +neighbor results identify the selected declarations. This label-only arm does +not measure the best possible longer agent workflow. + +All 58 executed tool calls succeed. Call text totals are 139,718 bytes for +Compass and 51,605 for Graphify; complete captured session bytes are 232,183 +and 94,443. Unequal community sizes and success counts still preclude a +matched-success efficiency claim. The public inputs, outputs and product +version remain compatible at 0.3.30. + +Verification: + +- Both failing exact-priority/collision-count regressions are preserved in the + initial log; the four focused neighbor tests then pass. +- All 58 MCP tests pass, including 38 library tests and 20 integration tests. +- Workspace library/binary tests: **1,101 pass, two ignored**. The MCP library + tests are included in this count, not additive. +- Product contract tests: **nine pass**. Workspace formatting, Clippy with + warnings denied, product boundary and the CLI build pass. +- Developer harness: **120 tests pass**. A comparison verifier exposed unstable + ordering in older missing-neighbor diagnostic lists. The auditor now sorts + missing/extra lists; the failing regression and initial capture remain. + Final recapture preserves all public responses and verdicts. Older diagnostic + list ordering is canonicalized only for comparison, not source interpretation. +- The first full MCP run also exposed a stale transport assertion from before + the shared renderer's compact `RESULT` header. Commit `500a4565` updates it + and verifies text equality with the rendered structured Agent View. The + failed run remains recorded; the complete rerun passes. + +Validation logs retain the existing core-test `unused_mut` warning and a macOS +linker unwind-table warning. No extraction, language or viewer code changes, so +those full qualification/JavaScript gates were not rerun. Source assertion +precision, god-object judgments, broader community usefulness, longer walks +and held-out confirmation remain incomplete. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From f660408bcead5e4fc44fdcd61b22c5635fe2faa3 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 00:57:10 -0700 Subject: [PATCH 54/97] test: freeze source-anchored public navigation workflow --- benchmarks/agent_query/COVERAGE_PLAN.md | 12 ++++ ...community_identity_navigation_panel_a.json | 68 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 benchmarks/agent_query/community_identity_navigation_panel_a.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index fa9dbf900..5192e33cd 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -236,3 +236,15 @@ and community texts stay unchanged. Keep the two member-selection ambiguities, five genuine neighbor ambiguities and missing WalkDir call visible. A later workflow should use each tool's documented source-qualified or exact-ID handles; this label-only arm is not a best-possible agent navigation score. + +### Source-anchored public navigation + +`community_identity_navigation_panel_a.json` freezes a stronger two-follow-up +arm over the same tasks: community membership, one public resolver, then one +neighbor lookup using only the returned source-matched ID. Compass uses typed +`search_symbols`; Graphify uses `get_node` with its documented `path::symbol` +form. Both receive the exact task file, declaration start and terminal symbol. +Missing, ambiguous, mismatched or truncated resolver evidence stops navigation. +Report validated requested IDs separately from name-only neighbor output and +from exact collaborator identity. Different native controls and projected +fields remain explicit; this is neither equal-token nor held-out evidence. diff --git a/benchmarks/agent_query/community_identity_navigation_panel_a.json b/benchmarks/agent_query/community_identity_navigation_panel_a.json new file mode 100644 index 000000000..0402ae137 --- /dev/null +++ b/benchmarks/agent_query/community_identity_navigation_panel_a.json @@ -0,0 +1,68 @@ +{ + "schema": "compass.community-identity-navigation-policy/1", + "scope": "Development workflow on the same 15 source-defined task pairs, frozen before this source-anchored resolver-to-neighbor capture. Reused repositories, not held-out. Different public tool names represent equivalent resolution and navigation roles.", + "registrationSha256": "71faed0f477ee97da98c8bbb86686fae09de450664e4e000d9b2404496b94b6d", + "startingInput": "For every task, prepare the community containing the first exact source declaration symmetrically from each stored graph. Preparation is not scored as retrieval. Missing, ambiguous or unassigned inputs remain unresolved.", + "publicTaskInput": "The first declaration file, startLine and terminal symbol are supplied task coordinates to both tools. The target collaborator is scoring data, never used to select or issue requests.", + "memberGate": "Call get_community on the prepared community. Retain every row whose exact source file and terminal symbol match the public task seed. If none match, stop unresolved. If several display-label variants match, retain all and use their common exact terminal symbol; do not choose a particular declaration or label.", + "resolveCalls": { + "compass": { + "method": "search_symbols", + "query": "The common returned terminal symbol.", + "limits": { + "max_candidates": 256, + "max_nodes": 500, + "max_response_bytes": 524288 + } + }, + "graphify": { + "method": "get_node", + "label": "Exact returned source file plus :: plus the common terminal symbol. This path-scoped form is documented in its public ambiguity responses." + } + }, + "idSelection": "Compass: use only structured compass.mcp.tool-result/1 -> compass.query/1 search result node IDs present in both results and nodes; reject transport or semantic truncation. Match exact returned source file, declaration start line and terminal symbol. Retain all matched IDs. Graphify: parse exactly one Node heading, ID and Source file plus L-start line from get_node; require the same exact coordinates. For either tool, issue the next call only when exactly one matching ID is returned. Never resolve a collision by score, output order, degree, graph inspection or desired adjacency.", + "followup": { + "tool": "get_neighbors", + "label": "The returned, uniquely source-matched ID.", + "relation_filter": "calls", + "maximumCallsAfterCommunity": 2, + "additionalRetries": 0 + }, + "bounds": { + "requestTimeoutSeconds": 60, + "maxResponseBytes": 1048576, + "maxSessionBytes": 67108864, + "graphifyTokenBudget": 262144 + }, + "budgetInterpretation": "Common external 60-second/1-MiB response and 64-MiB session bounds. Compass search uses explicit native candidate/node/semantic-byte bounds; Graphify get_node has its fixed single-node/attribute projection. Graphify community and neighbor calls receive the same generous explicit token allowance as earlier runs. Native controls and output richness differ. Report full calls, errors and bytes; do not claim equal token budget or latency efficiency.", + "scoring": [ + "Retain all 15 tasks and the same 14 direct-call tasks. Missing, mismatched, ambiguous or truncated resolver output stays unresolved.", + "After all requests, verify the returned selected ID against the frozen exact source-declaration oracle. Report resolver success separately from a completed neighbor lookup with that validated ID and the expected heading.", + "Keep the older name-only seed/target uniqueness audit separately. A correct requested ID does not make a successful legacy neighbor response self-identifying; both tools still render seed/neighbor display names. Never use matching adjacency to choose an identity.", + "For a validated seed-ID lookup, report the reviewed outgoing collaborator label and calls relation separately from globally unambiguous target-label identity. Do not infer a target definition from an edge call-site anchor. Report stored direct-call evidence separately.", + "Check displayed direction/neighbor-label multiplicity against distinct graph neighbors after calls filtering. It is graph projection consistency, not all-assertion source precision or call occurrence recall.", + "Starting communities are prepared; initial retrieval is unscored. Unequal split-community subsets are descriptive only." + ], + "directCallTasks": [ + "chi-router-construction", + "chi-route-pattern-parameters", + "click-atomic-close", + "click-terminal-string-width", + "click-help-definition-layout", + "jsoup-safe-document-copy", + "jsoup-html-fragment-parse", + "jsoup-html-whitespace", + "redux-listener-registration", + "redux-action-shape-validation", + "redux-action-creator-binding", + "walkdir-directory-handle-budget", + "walkdir-deferred-directory-depth", + "walkdir-symlink-loop-detection" + ], + "limitations": [ + "No retries beyond one resolver and one neighbor call; other public workflows remain possible.", + "Exact source start lines are provided task inputs. This is not natural-language seed discovery.", + "A resolver can return a plausible but wrong same-file declaration; returned anchors are checked before navigation and graph/source identity is checked only afterward.", + "Full member/neighbor identity projections, source assertion precision, god-object diagnosis, longer walks, and held-out confirmation remain unmeasured." + ] +} From c3d4cc265b42ee90e8a8bb38a561c0a16f7d6bcb Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 01:08:14 -0700 Subject: [PATCH 55/97] test: audit source-assisted public navigation across five languages --- benchmarks/agent_query/COVERAGE_PLAN.md | 8 + benchmarks/agent_query/README.md | 25 + benchmarks/agent_query/community_identity.py | 83 +++ ...ty_identity_navigation_review_panel_a.json | 634 ++++++++++++++++++ .../agent_query/community_navigation.py | 55 +- .../tests/test_community_identity.py | 83 +++ .../tests/test_community_navigation.py | 9 + ...ode-graph-intelligence-audit-2026-09-26.md | 69 ++ 8 files changed, 954 insertions(+), 12 deletions(-) create mode 100644 benchmarks/agent_query/community_identity.py create mode 100644 benchmarks/agent_query/community_identity_navigation_review_panel_a.json create mode 100644 benchmarks/agent_query/tests/test_community_identity.py diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 5192e33cd..b77f396fb 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -248,3 +248,11 @@ Missing, ambiguous, mismatched or truncated resolver evidence stops navigation. Report validated requested IDs separately from name-only neighbor output and from exact collaborator identity. Different native controls and projected fields remain explicit; this is neither equal-token nor held-out evidence. + +The final `community-identity-navigation-02` capture resolves the exact seed ID +and completes neighbor lookup on all 15 tasks for both tools. The review records +13/14 versus 14/14 displayed collaborators, with three ambiguous target labels +on each side. The remaining direct-call gap is Compass's WalkDir indexed +receiver call. Keep explicit destination identity, source precision and resolver +output cost as separate next questions; existing APIs already overcome the +member-label ambiguities with this extra lookup and supplied source coordinates. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 5c568fde5..56a30914e 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -429,3 +429,28 @@ neighbor texts remain identical. Missing/extra neighbor diagnostic arrays now sort deterministically; older captures need order normalization for those two arrays only. Genuine collisions, two ambiguous community selectors and the missing WalkDir call remain open. + +### Source-coordinate-assisted resolver workflow + +Use `community_identity_navigation_panel_a.json` as `--policy` with the same +`community_navigation` collector. It is frozen in `f660408b`. After community +membership, Compass uses structured `search_symbols` results and Graphify uses +`get_node` with its documented `path::symbol` selector. Both policies receive +the exact seed file, declaration start and terminal symbol, then pass only a +uniquely source-matched returned ID to `get_neighbors`. A search result that is +truncated or whose matching IDs remain ambiguous cannot supply the next input. +The expected collaborator is scoring-only and cannot gate requests. + +The matching review records correct seed IDs and completed neighbor lookups on +15/15 tasks for each tool. Compass displays the reviewed collaborator label on +13/14 direct-call tasks; Graphify on 14/14. Three target labels remain ambiguous +in each tool's neighbor text, leaving 10/14 and 11/14 unambiguous target labels. +The missing WalkDir call remains a graph gap. Earlier label-only failures do +not imply that these existing resolver APIs cannot complete seed navigation. + +All 90 tool calls succeed, and all 129 harness tests pass. Complete session bytes +are 785,989 for Compass and 102,213 for Graphify under this fixed policy. Compass +search returns more candidates and structured evidence; native bounds and +semantic payloads differ. Report these actual workflow costs with that context. +This is source-assisted development evidence, not natural-language discovery, +comprehensive assertion precision, or held-out performance. diff --git a/benchmarks/agent_query/community_identity.py b/benchmarks/agent_query/community_identity.py new file mode 100644 index 000000000..2b539884d --- /dev/null +++ b/benchmarks/agent_query/community_identity.py @@ -0,0 +1,83 @@ +"""Select public source-anchored resolver inputs/IDs without graph access.""" +import re + +from benchmarks.agent_query.community_navigation import select_label +from benchmarks.agent_query.runner import _node_anchor, _terminal_symbol + +SEARCH_LIMITS = dict(max_candidates=256, max_nodes=500, max_response_bytes=524288) + + +def resolver_request(tool, text, seed): + selection = select_label(text, seed) + # Several presentation variants may refer to the same terminal symbol. No + # declaration is chosen here: the public resolver must return its anchors. + if not selection['matchingRows']: + return dict(selection=selection, request=None) + symbol = seed['symbol'] + if tool == 'compass': + request = dict(method='search_symbols', arguments=dict(query=symbol, **SEARCH_LIMITS)) + elif tool == 'graphify': + if '::' in seed['file']: + return dict(selection=selection, request=None, reason='unsupported path delimiter') + request = dict(method='get_node', arguments=dict(label=seed['file']+'::'+symbol)) + else: + raise ValueError('unsupported tool') + return dict(selection=selection, request=request) + + +def selected_id(tool, call, seed): + result = dict(status='invalid', matchedIds=[], selector=None, returnedAnchors=[]) + if not call.get('executionSucceeded'): + result['status'] = 'tool-error' + return result + if tool == 'compass': + envelope = call.get('response', {}).get('result', {}).get('structuredContent') + if not isinstance(envelope, dict) or envelope.get('schema') != 'compass.mcp.tool-result/1': + return result + body = envelope.get('result') + transport = envelope.get('transportTruncation') + if (not isinstance(body, dict) or body.get('schema') != 'compass.query/1' + or body.get('operation') != 'search' or not isinstance(transport, dict)): + return result + if body.get('truncated') is not False or transport.get('truncated') is not False: + result['status'] = 'truncated-or-unknown' + return result + diagnostics = body.get('diagnostics', []) + if not isinstance(diagnostics, list) or len(diagnostics) > 1024 or any(not isinstance(d, dict) for d in diagnostics): + return result + if any(d.get('code') == 'bounded_truncation' for d in diagnostics): + result['status'] = 'truncated-or-unknown' + return result + nodes, hits = body.get('nodes'), body.get('results') + if not isinstance(nodes, list) or not isinstance(hits, list) or len(nodes) > 500 or len(hits) > 256: + return result + if any(not isinstance(n, dict) or not isinstance(n.get('id'), str) or not n['id'] for n in nodes): + return result + ids = [n['id'] for n in nodes] + if len(ids) != len(set(ids)) or any(not isinstance(h, dict) or h.get('nodeId') not in ids for h in hits): + return result + hit_ids = {h['nodeId'] for h in hits} + anchors = [] + for node in nodes: + if node['id'] in hit_ids: + file, line, names = _node_anchor(node, 'compass') + anchors.append(dict(id=node['id'], file=file, line=line, symbols=sorted(names))) + elif tool == 'graphify': + text = call.get('text', '') + labels = re.findall(r'^Node: (.*)$', text, re.M) + ids = re.findall(r'^ ID: (.+)$', text, re.M) + sources = re.findall(r'^ Source: (.+) L([1-9][0-9]*)(?:-L[1-9][0-9]*)?$', text, re.M) + if len(labels) != 1 or len(ids) != 1 or len(sources) != 1: + result['status'] = 'unresolved-response' + return result + anchors = [dict(id=ids[0], file=sources[0][0], line=int(sources[0][1]), + symbols=[_terminal_symbol(labels[0])])] + else: + raise ValueError('unsupported tool') + result['returnedAnchors'] = anchors + result['matchedIds'] = sorted({a['id'] for a in anchors if a['file'] == seed['file'] + and a['line'] == seed['startLine'] and seed['symbol'] in a['symbols']}) + matches = result['matchedIds'] + result['status'] = 'resolved' if len(matches) == 1 else 'anchor-mismatch' if not matches else 'ambiguous' + result['selector'] = matches[0] if len(matches) == 1 else None + return result diff --git a/benchmarks/agent_query/community_identity_navigation_review_panel_a.json b/benchmarks/agent_query/community_identity_navigation_review_panel_a.json new file mode 100644 index 000000000..1e3514de5 --- /dev/null +++ b/benchmarks/agent_query/community_identity_navigation_review_panel_a.json @@ -0,0 +1,634 @@ +{ + "schema": "compass.community-identity-navigation-review/1", + "scope": "Source-coordinate-assisted public MCP workflow on the same 15 development tasks. Exact seed file, start line and symbol are supplied to both policies; this is not natural-language seed discovery.", + "protocolCommit": "f660408b", + "capture": "community-identity-navigation-02/capture.json", + "captureSha256": "50910af023897886c683fcffc9228ef894303f56659c42030c573e9b92b7a3f9", + "initialCapture": "community-identity-navigation-01/capture.json", + "serverExecutableSha256": { + "compass": "1174ab1535e4614e08b04a283a90fe33ee53e82517907ff85c323cacb6d9b75a", + "graphify": "bc9a27bd52c265e86fcd73ef1d4165ec8a47695cffb7624cb0ee618dca83e6c0" + }, + "policySha256": "4c23238e91f506c1bdf2ad4519789ca7a6fa42a7103828d49bc5ffd6de65450a", + "registrationSha256": "71faed0f477ee97da98c8bbb86686fae09de450664e4e000d9b2404496b94b6d", + "sourceRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "summary": { + "compass": { + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 13, + "unambiguousTargetLabel": 10, + "callTextBytes": 156976, + "callResponseWireBytes": 694354, + "fullSessionBytesIncludingRequestsAndStderr": 785989 + }, + "graphify": { + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11, + "callTextBytes": 55402, + "callResponseWireBytes": 60410, + "fullSessionBytesIncludingRequestsAndStderr": 102213 + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "task": "chi-router-construction", + "returnedSeedId": "sha256:1928266631ebdc4651cac18aa5abc7ab261abc74a669ec9071a53d68b1f30699", + "expectedTargetId": "sha256:fd36852628b4b3873d779bdb534cf23a17b6908247f6c46db80dbac5b2d7c453", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 6505, + "callResponseWireBytes": 108798 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-request-identity", + "returnedSeedId": "sha256:394032841e0823b74d28b8a866c2eb3cd7618a345c06b08fc66e63e5657d5ae4", + "expectedTargetId": "sha256:34078dccfae1ef1443550189102e7927d10009b45d211741895af2b59e44907e", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": false, + "reviewedCollaboratorLabelPresent": false, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 0, + "displayedAdjacencyConsistent": true, + "callTextBytes": 2496, + "callResponseWireBytes": 6311 + }, + { + "repository": "chi", + "tool": "compass", + "task": "chi-route-pattern-parameters", + "returnedSeedId": "sha256:6df5a320c50c2a4cc16144d1db213eeefc7cf84eced0fd9b1fbedcd21553b212", + "expectedTargetId": "sha256:111dda918f957e920843303a5c69f2cda3e9b2006d711b0aef4efca1545bdc62", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1808, + "callResponseWireBytes": 5581 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-router-construction", + "returnedSeedId": "chi_newrouter", + "expectedTargetId": "mux_newmux", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 8162, + "callResponseWireBytes": 8594 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-request-identity", + "returnedSeedId": "middleware_request_id_requestid", + "expectedTargetId": "middleware_request_id_getreqid", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": false, + "reviewedCollaboratorLabelPresent": false, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 0, + "displayedAdjacencyConsistent": true, + "callTextBytes": 2388, + "callResponseWireBytes": 2721 + }, + { + "repository": "chi", + "tool": "graphify", + "task": "chi-route-pattern-parameters", + "returnedSeedId": "tree_patparamkeys", + "expectedTargetId": "tree_patnextsegment", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1230, + "callResponseWireBytes": 1552 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-atomic-close", + "returnedSeedId": "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4", + "expectedTargetId": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 5, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 10538, + "callResponseWireBytes": 48472 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-terminal-string-width", + "returnedSeedId": "sha256:b11122339a79629b59028324039bb74faf1857f56629c753c1e51abc3c93d330", + "expectedTargetId": "sha256:84776cf5b684f5564ad6dc1143881454a6dd6f12faf2f62a8b1451fc9c89dbd2", + "splitCommunity": true, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 5469, + "callResponseWireBytes": 10657 + }, + { + "repository": "click", + "tool": "compass", + "task": "click-help-definition-layout", + "returnedSeedId": "sha256:3883ba20e7d4cfe17c813110a793c1d9db32455194d42971a01787a2b45c357e", + "expectedTargetId": "sha256:8e5a66041c4ec1628dcf55670a1c379436eec6a47f92a8a45608693bc2934e5e", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 5655, + "callResponseWireBytes": 13787 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-atomic-close", + "returnedSeedId": "src_click_compat_atomicfile_exit", + "expectedTargetId": "src_click_compat_atomicfile_close", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 5, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 539, + "callResponseWireBytes": 824 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-terminal-string-width", + "returnedSeedId": "src_click_compat_term_len", + "expectedTargetId": "src_click_compat_strip_ansi", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 2525, + "callResponseWireBytes": 2835 + }, + { + "repository": "click", + "tool": "graphify", + "task": "click-help-definition-layout", + "returnedSeedId": "src_click_formatting_helpformatter_write_dl", + "expectedTargetId": "src_click_formatting_measure_table", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1120, + "callResponseWireBytes": 1412 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-safe-document-copy", + "returnedSeedId": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "expectedTargetId": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 15131, + "callResponseWireBytes": 231288 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-fragment-parse", + "returnedSeedId": "sha256:116e34df3ea1beb0e4e87a6fbc8c2a4667bcac6135618616b616a6990d10e729", + "expectedTargetId": "sha256:d51486603fd23ace9d94cf199d726965909177f01ed78bd3ed9a3ca39436412b", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 5, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 64622, + "callResponseWireBytes": 72908 + }, + { + "repository": "jsoup", + "tool": "compass", + "task": "jsoup-html-whitespace", + "returnedSeedId": "sha256:7b70b1683af87dcfb443f8d622d324166f6a6a9d6d40e66b0f8baf1eea8ebbd7", + "expectedTargetId": "sha256:8b362a6fd1d3e3687e87d8010b0acf4af64a9bdf2c0cbd1ba51760278afb3227", + "splitCommunity": true, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 3, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 13286, + "callResponseWireBytes": 22301 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-safe-document-copy", + "returnedSeedId": "src_main_java_org_jsoup_safety_cleaner_cleaner_clean", + "expectedTargetId": "src_main_java_org_jsoup_safety_cleaner_cleaner_copysafenodes", + "splitCommunity": true, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 6380, + "callResponseWireBytes": 6726 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-fragment-parse", + "returnedSeedId": "src_main_java_org_jsoup_parser_parser_parser_parsebodyfragment", + "expectedTargetId": "src_main_java_org_jsoup_parser_parser_parser_parsefragment", + "splitCommunity": true, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 3, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 20399, + "callResponseWireBytes": 20913 + }, + { + "repository": "jsoup", + "tool": "graphify", + "task": "jsoup-html-whitespace", + "returnedSeedId": "src_main_java_org_jsoup_internal_stringutil_stringutil_isblank", + "expectedTargetId": "src_main_java_org_jsoup_internal_stringutil_stringutil_iswhitespace", + "splitCommunity": true, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 3, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 6903, + "callResponseWireBytes": 7271 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-listener-registration", + "returnedSeedId": "sha256:f27566baebab97a78ea7b6d143149ecdff76b39167fb12b42612a3e69fba5a5d", + "expectedTargetId": "sha256:692fe8cbc7b8e5a03bcb79c20531aefdbfa31098ea43bbe9b0301d61cd869f27", + "splitCommunity": false, + "memberMatchingRows": 3, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 2, + "displayedAdjacencyConsistent": true, + "callTextBytes": 4341, + "callResponseWireBytes": 30594 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-shape-validation", + "returnedSeedId": "sha256:237b20392b78b4972302af1924657a5709e90d5534fbdaee1ac633567db87377", + "expectedTargetId": "sha256:08c1f8776bb6ac1c79c535fe70f0341ba85fb12fcf8ab56171ee2281e3f6fcb9", + "splitCommunity": false, + "memberMatchingRows": 2, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 10857, + "callResponseWireBytes": 34820 + }, + { + "repository": "redux", + "tool": "compass", + "task": "redux-action-creator-binding", + "returnedSeedId": "sha256:872a5ee2bce2daedae73fa970870b29f3d0e02033199aa263da175827b5899fe", + "expectedTargetId": "sha256:118ac793f4d6b77676a6543f79b8cdef6cd785fef2e27dcaee49c6568ff3488d", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 2, + "displayedAdjacencyConsistent": true, + "callTextBytes": 3798, + "callResponseWireBytes": 64497 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-listener-registration", + "returnedSeedId": "src_createstore_createstore_subscribe", + "expectedTargetId": "src_createstore_createstore_ensurecanmutatenextlisteners", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 889, + "callResponseWireBytes": 1181 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-shape-validation", + "returnedSeedId": "src_utils_isaction_isaction", + "expectedTargetId": "src_utils_isplainobject_isplainobject", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 572, + "callResponseWireBytes": 856 + }, + { + "repository": "redux", + "tool": "graphify", + "task": "redux-action-creator-binding", + "returnedSeedId": "src_bindactioncreators_bindactioncreators", + "expectedTargetId": "src_bindactioncreators_bindactioncreator", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1068, + "callResponseWireBytes": 1362 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-directory-handle-budget", + "returnedSeedId": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "expectedTargetId": "sha256:ff9fee2d67be76e5af714ef0bceb4d68be41efd8086deccc6a46eb19cd9ee0f9", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": false, + "unambiguousTargetLabel": false, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 0, + "displayedAdjacencyConsistent": true, + "callTextBytes": 3825, + "callResponseWireBytes": 7681 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-deferred-directory-depth", + "returnedSeedId": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "expectedTargetId": "sha256:648e4f3a42dcd72aa98ad5725459ca59362c53374b257faa9cd00a5cfee708f0", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 3782, + "callResponseWireBytes": 7697 + }, + { + "repository": "walkdir", + "tool": "compass", + "task": "walkdir-symlink-loop-detection", + "returnedSeedId": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "expectedTargetId": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 4863, + "callResponseWireBytes": 28962 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-directory-handle-budget", + "returnedSeedId": "src_lib_intoiter_push", + "expectedTargetId": "src_lib_dirlist_close", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1175, + "callResponseWireBytes": 1488 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-deferred-directory-depth", + "returnedSeedId": "src_lib_intoiter_get_deferred_dir", + "expectedTargetId": "src_lib_intoiter_skippable", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 1093, + "callResponseWireBytes": 1404 + }, + { + "repository": "walkdir", + "tool": "graphify", + "task": "walkdir-symlink-loop-detection", + "returnedSeedId": "src_lib_intoiter_follow", + "expectedTargetId": "src_lib_intoiter_check_loop", + "splitCommunity": false, + "memberMatchingRows": 1, + "resolverStatus": "resolved", + "sourceMatchedSeedLookupCompleted": true, + "directCallRequired": true, + "reviewedCollaboratorLabelPresent": true, + "unambiguousTargetLabel": true, + "globalTargetLabelIdentities": 1, + "storedDirectCallRecords": 1, + "displayedAdjacencyConsistent": true, + "callTextBytes": 959, + "callResponseWireBytes": 1271 + } + ], + "interpretation": [ + "Each side returns the expected exact seed ID on all 15 tasks and completes a neighbor lookup using that ID. The earlier label-only failures do not establish that public navigation is impossible.", + "Compass uses search_symbols structured search hits/anchors; Graphify uses its documented get_node path::symbol form. The equal allowance is one resolver plus one neighbor call after the prepared community, not identical API names or native payload controls.", + "The completed lookup metric verifies the requested ID and successful expected heading. It does not claim that legacy neighbor output explicitly returns the seed ID. The older global-name-only identity check remains separately present in raw audits.", + "Compass shows the reviewed outgoing collaborator label on 13/14 direct-call tasks; Graphify on 14/14. Compass still lacks the source-supported WalkDir push -> DirList.close call at src/lib.rs:906 in the frozen graph.", + "Three target labels are ambiguous graph-wide in both products: Click close, jsoup parseFragment, and jsoup isWhitespace. Matching text is not exact target-definition evidence. Excluding those three gives 10/14 Compass versus 11/14 Graphify unambiguous target labels after source-matched seed lookup.", + "All five earlier split-community pairs are now navigable at the displayed-label level (two Compass, three Graphify). Those unequal subsets are descriptive and are not an additional comparative denominator.", + "This fixed workflow exchanges substantially more bytes for Compass. Its structured search returns multiple candidates, source anchors and richer evidence; native controls and returned semantic volume differ. The recorded cost is real for this policy, not a universal efficiency ranking.", + "No product code changed in this arm. These gains come from using existing public resolution capabilities with supplied source coordinates; they are not attributed to a new extraction fix." + ], + "verification": { + "calls": "All 90 executed public tool calls succeed: 30 community, 30 resolver and 30 neighbor calls. No timeout, response-cap failure or additional retry. All community memberships and displayed adjacency projections match their stored graphs.", + "tests": "129 developer-harness tests pass, including eight new resolver tests and one missing-target-oracle scorer test.", + "independentRecomputation": "A separate same-agent verifier checks saved requests/responses, support-file hashes, exact source coordinates, returned ID provenance, graph IDs and displayed neighbor multiplicity. It is not an independent semantic reviewer.", + "recapture": "After removing target-oracle availability from the new workflow control flow, final recapture retains all 90 tool response packets and all 30 task audits identically. The initial capture is preserved.", + "rust": "Not rerun: this iteration changes only developer evaluation and documentation, using the previously frozen and validated 0.3.30 binary." + }, + "nextEvidence": [ + "Resolve the reviewed indexed-receiver WalkDir call at the owning language/resolver boundary, with negative/ambiguity regressions and full extraction qualification.", + "Evaluate explicit target identities and source anchors in neighbor/call results; do not equate duplicate displayed labels with exact destinations.", + "Evaluate resolver cost with bounded source-aware lookup while preserving overload ambiguity.", + "Continue source-defined god-object evidence, community responsibility quality, directed/longer walks and fresh held-out confirmation." + ] +} diff --git a/benchmarks/agent_query/community_navigation.py b/benchmarks/agent_query/community_navigation.py index 4fa4e64eb..79857c1f8 100644 --- a/benchmarks/agent_query/community_navigation.py +++ b/benchmarks/agent_query/community_navigation.py @@ -37,14 +37,15 @@ def select_label(text: str, seed: dict) -> dict: status='selected' if len(choices) == 1 else 'missing' if not choices else 'ambiguous') -def score_navigation(text: str, graph: dict, tool: str, seed: str, target: str, +def score_navigation(text: str, graph: dict, tool: str, seed: str, target: str | None, succeeded: bool) -> dict: """Never identify a duplicate label by its conveniently matching neighbors.""" nodes = {node['id']: node for node in graph['nodes']} names = defaultdict(list) for node in nodes.values(): names[label(node, tool)].append(node['id']) - seed_name, target_name = label(nodes[seed], tool), label(nodes[target], tool) + seed_name = label(nodes[seed], tool) + target_name = label(nodes[target], tool) if target in nodes else None heading = text.splitlines()[0] if text else '' header_matches = succeeded and heading == f'Neighbors of {seed_name}:' actual = Counter((direction, name) for direction, name, relation in NEIGHBOR.findall(text) @@ -64,7 +65,7 @@ def score_navigation(text: str, graph: dict, tool: str, seed: str, target: str, pairs.add(('<--', a)) expected = Counter((direction, label(nodes[identifier], tool)) for direction, identifier in pairs) identity = header_matches and names[seed_name] == [seed] - displayed = header_matches and actual['-->', target_name] > 0 + displayed = header_matches and target_name is not None and actual['-->', target_name] > 0 return dict(seedHeadingMatches=header_matches, ambiguityReported=bool(re.search(r'\bambiguous\b', text, re.I)), seedLabelCandidates=sorted(names[seed_name]), seedIdentitySupported=identity, @@ -101,7 +102,8 @@ def execute(args): registration_bytes = read_bounded(args.registration, MAX_SOURCE_BYTES) run_bytes = read_bounded(args.run, MAX_JSON_BYTES) policy, registration, run = map(json.loads, [policy_bytes, registration_bytes, run_bytes]) - if policy.get('schema') != 'compass.community-navigation-policy/1': + identity_mode = policy.get('schema') == 'compass.community-identity-navigation-policy/1' + if policy.get('schema') != 'compass.community-navigation-policy/1' and not identity_mode: raise ValueError('unsupported workflow policy') if registration.get('schema') != 'compass.community-task-pairs/1': raise ValueError('unsupported task registration') @@ -110,6 +112,10 @@ def execute(args): if policy['bounds'] != dict(requestTimeoutSeconds=60, maxResponseBytes=1048576, maxSessionBytes=67108864, graphifyTokenBudget=262144): raise ValueError('unsupported workflow bounds') + if identity_mode: + from benchmarks.agent_query.community_identity import SEARCH_LIMITS, resolver_request, selected_id + if policy['resolveCalls']['compass']['limits'] != SEARCH_LIMITS: + raise ValueError('unsupported resolver limits') repositories = registration['repositories'] if not 1 <= len(repositories) <= 32 or len({r['repository'] for r in repositories}) != len(repositories): raise ValueError('invalid repository list') @@ -119,7 +125,7 @@ def execute(args): raise ValueError('invalid direct-call task list') verify_environment(args) args.output.mkdir(parents=True, exist_ok=False) - report = dict(schema='compass.community-navigation-capture/1', complete=False, + report = dict(schema='compass.community-identity-navigation-capture/1' if identity_mode else 'compass.community-navigation-capture/1', complete=False, policySha256=digest(policy_bytes), registrationSha256=digest(registration_bytes), sourceRunSha256=digest(run_bytes), sourceRun=str(args.run.resolve()), graphifyEnvironmentSha256=_sha256_file(args.graphify_environment), @@ -128,7 +134,7 @@ def execute(args): report['servers'][tool] = dict(path=str(path), sha256=_sha256_file(path)) for path in [args.policy, args.registration, args.graphify_environment, *[ Path(__file__).with_name(name) for name in ['community_navigation.py', 'community_tasks.py', - 'mcp_transport.py', 'mcp_compare.py', 'mcp_audit.py', 'runner.py']]]: + 'mcp_transport.py', 'mcp_compare.py', 'mcp_audit.py', 'runner.py', 'community_identity.py']]]: shutil.copy2(path, args.output/path.name) report['supportFiles'][path.name] = _sha256_file(path) def save(): @@ -161,7 +167,10 @@ def save(): session.initialize() listing = session.send('tools/list', {}) advertised = {t['name'] for t in listing.get('result', {}).get('tools', [])} - if not {'get_community', 'get_neighbors'} <= advertised: + required = {'get_community', 'get_neighbors'} + if identity_mode: + required.add('search_symbols' if tool == 'compass' else 'get_node') + if not required <= advertised: raise ValueError('required public tools unavailable') except ERRORS as error: failure = str(error) @@ -174,10 +183,10 @@ def save(): graphSha256=graph_hash, directCallRequired=task['id'] in direct) if failure is not None: row['captureError'] = 'connection unavailable: '+failure - elif seed['status'] != 'resolved' or target['status'] != 'resolved': + elif seed['status'] != 'resolved' or (not identity_mode and target['status'] != 'resolved'): row['inputUnresolved'] = True else: - row['splitCommunity'] = seed['community'] != target['community'] + row['splitCommunity'] = (seed['community'] != target['community']) if target['status'] == 'resolved' else None community = call(session, 'get_community', dict(community_id=seed['community'], **budget)) row['communityCall'] = community if 'captureError' in community: @@ -186,13 +195,35 @@ def save(): # The selector function has no graph access. selection = select_label(community['text'], task['declarations'][0]) row['selection'] = selection - if selection['selector'] is not None: - neighbors = call(session, 'get_neighbors', dict(label=selection['selector'], relation_filter='calls', **budget)) + selector = selection['selector'] + if identity_mode: + prepared_request = resolver_request(tool, community['text'], task['declarations'][0]) + row['resolverSelection'] = prepared_request + selector = None + request = prepared_request['request'] + if request is not None: + resolved = call(session, request['method'], request['arguments']) + row['resolverCall'] = resolved + if 'captureError' in resolved: + failure = resolved['captureError'] + choice = selected_id(tool, resolved, task['declarations'][0]) + row['idSelection'] = choice + selector = choice['selector'] + if selector is not None: + neighbors = call(session, 'get_neighbors', dict(label=selector, relation_filter='calls', **budget)) row['neighborCall'] = neighbors if 'captureError' in neighbors: failure = neighbors['captureError'] row['audit'] = score_navigation(neighbors.get('text', ''), graph, tool, - seed['matchedNodeIds'][0], target['matchedNodeIds'][0], neighbors['executionSucceeded']) + seed['matchedNodeIds'][0], target['matchedNodeIds'][0] if len(target['matchedNodeIds']) == 1 else None, neighbors['executionSucceeded']) + if identity_mode: + checked = row['audit'] + checked['returnedIdMatchesSourceOracle'] = selector == seed['matchedNodeIds'][0] + completed = checked['returnedIdMatchesSourceOracle'] and checked['seedHeadingMatches'] + checked['sourceMatchedSeedLookupCompleted'] = completed + checked['addressedCollaboratorLabelPresent'] = completed and checked['collaboratorDisplayed'] + checked['addressedCollaboratorIdentitySupported'] = (checked['addressedCollaboratorLabelPresent'] + and len(checked['collaboratorLabelCandidates']) == 1) # Scoring is after requests, never selector preparation. row['communityAudit'] = audit_membership(dict(repository=name, tool=tool, question='community', **community), graph) diff --git a/benchmarks/agent_query/tests/test_community_identity.py b/benchmarks/agent_query/tests/test_community_identity.py new file mode 100644 index 000000000..42942ccbc --- /dev/null +++ b/benchmarks/agent_query/tests/test_community_identity.py @@ -0,0 +1,83 @@ +import copy +import unittest + +from benchmarks.agent_query.community_identity import resolver_request, selected_id + + +class CommunityIdentityTests(unittest.TestCase): + seed = dict(file='src/a.rs', startLine=10, symbol='run') + + def compass(self, nodes=None): + if nodes is None: + nodes = [dict(id='a', name='run()', source=dict(file='src/a.rs', startLine=10))] + return dict(executionSucceeded=True, response=dict(result=dict(structuredContent=dict( + schema='compass.mcp.tool-result/1', transportTruncation=dict(truncated=False), + result=dict(schema='compass.query/1', operation='search', truncated=False, + nodes=nodes, results=[dict(nodeId=n['id']) for n in nodes], diagnostics=[]))))) + + def test_member_variants_use_common_symbol_without_selecting_declaration(self): + text = ' run [src/a.rs]\n .run() [src/a.rs]' + c = resolver_request('compass', text, self.seed) + self.assertEqual(c['selection']['status'], 'ambiguous') + self.assertEqual(c['request']['arguments']['query'], 'run') + g = resolver_request('graphify', text, self.seed) + self.assertEqual(g['request']['arguments']['label'], 'src/a.rs::run') + self.assertIsNone(resolver_request('compass', ' run [other.rs]', self.seed)['request']) + + def test_exact_source_match_returns_observed_id_for_both_tools(self): + self.assertEqual(selected_id('compass', self.compass(), self.seed)['selector'], 'a') + g = dict(executionSucceeded=True, text='Node: .run()\n ID: actual_id\n Source: src/a.rs L10\n Degree: 4') + self.assertEqual(selected_id('graphify', g, self.seed)['selector'], 'actual_id') + + def test_duplicate_exact_anchor_is_ambiguous_not_ranked(self): + nodes = self.compass()['response']['result']['structuredContent']['result']['nodes'] + nodes.append(dict(id='b', name='run()', source=dict(file='src/a.rs', startLine=10))) + for order in [nodes, list(reversed(nodes))]: + r = selected_id('compass', self.compass(order), self.seed) + self.assertEqual(r['matchedIds'], ['a', 'b']) + self.assertEqual(r['status'], 'ambiguous') + self.assertIsNone(r['selector']) + + def test_partial_source_symbol_or_different_start_cannot_select_id(self): + for change in [dict(name='runner()'), dict(source=dict(file='src/b.rs', startLine=10)), + dict(source=dict(file='src/a.rs', startLine=1, endLine=20)), + dict(source=dict(file='src/a.rs', startLine=True))]: + node = dict(id='a', name='run()', source=dict(file='src/a.rs', startLine=10)) + node.update(change) + self.assertIsNone(selected_id('compass', self.compass([node]), self.seed)['selector']) + for source in ['src/a.rs L9', 'src/b.rs L10', 'src/a.rs unknown']: + r = selected_id('graphify', dict(executionSucceeded=True,text='Node: run()\n ID: a\n Source: '+source),self.seed) + self.assertIsNone(r['selector']) + + def test_truncation_unknown_schema_and_tool_errors_fail_closed(self): + for layer in ['transport', 'semantic', 'diagnostic', 'schema', 'failure']: + call = self.compass() + e = call['response']['result']['structuredContent']; b = e['result'] + if layer == 'transport': e['transportTruncation']['truncated'] = True + elif layer == 'semantic': b['truncated'] = True + elif layer == 'diagnostic': b['diagnostics'] = [dict(code='bounded_truncation')] + elif layer == 'schema': b['schema'] = 'compass.query/99' + else: call['executionSucceeded'] = False + self.assertIsNone(selected_id('compass', call, self.seed)['selector']) + + def test_only_returned_search_hits_can_supply_ids(self): + call = self.compass() + call['response']['result']['structuredContent']['result']['results'] = [] + self.assertIsNone(selected_id('compass',call,self.seed)['selector']) + + def test_missing_hit_nodes_duplicate_ids_and_invalid_diagnostics_are_rejected(self): + for variant in ['missing', 'duplicate', 'diagnostic']: + call=self.compass(); b=call['response']['result']['structuredContent']['result'] + if variant=='missing': b['results']=[dict(nodeId='not-returned')] + elif variant=='duplicate': b['nodes'].append(copy.deepcopy(b['nodes'][0])) + else: b['diagnostics']=['not-an-object'] + self.assertEqual(selected_id('compass',call,self.seed)['status'],'invalid') + + def test_multiple_graphify_ids_and_ambiguity_text_are_unresolved(self): + for text in ['Ambiguous: run matches 2 nodes.', + 'Node: run()\n ID: a\n ID: b\n Source: src/a.rs L10']: + self.assertIsNone(selected_id('graphify',dict(executionSucceeded=True,text=text),self.seed)['selector']) + + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/agent_query/tests/test_community_navigation.py b/benchmarks/agent_query/tests/test_community_navigation.py index 9a5fd6783..8ec9e145b 100644 --- a/benchmarks/agent_query/tests/test_community_navigation.py +++ b/benchmarks/agent_query/tests/test_community_navigation.py @@ -74,6 +74,15 @@ def test_parallel_calls_are_distinct_neighbors_not_occurrence_recall(self): self.assertEqual(result['expectedDisplayedNeighbors'], 1) self.assertTrue(result['displayedAdjacencyMatches']) + def test_missing_target_oracle_does_not_hide_valid_seed_navigation(self): + result = score_navigation('Neighbors of Runner.run():\n --> finish() [calls] [exact]', + self.graph(), 'compass', 'a', None, True) + self.assertTrue(result['seedIdentitySupported']) + self.assertTrue(result['displayedAdjacencyMatches']) + self.assertFalse(result['collaboratorDisplayed']) + self.assertFalse(result['collaboratorIdentitySupported']) + self.assertEqual(result['collaboratorLabelCandidates'], []) + def test_mismatch_diagnostics_have_stable_sorted_order(self): graph = self.graph() graph['nodes'].extend([dict(id='x', name='Zulu()'), dict(id='y', name='Alpha()')]) diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 84d10e226..cc7002e23 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2143,6 +2143,75 @@ those full qualification/JavaScript gates were not rerun. Source assertion precision, god-object judgments, broader community usefulness, longer walks and held-out confirmation remain incomplete. +### Source-coordinate-assisted public navigation + +Commit `f660408b` freezes a stronger +[public resolver workflow](../../benchmarks/agent_query/community_identity_navigation_panel_a.json) +before capture. Both tools receive the exact task seed file, declaration start +line and terminal symbol. Starting communities are still prepared symmetrically. +After finding matching member rows, Compass uses structured `search_symbols` +results and Graphify uses `get_node` with its documented `path::symbol` form. +Only a uniquely source-matched returned ID becomes the next `get_neighbors` +input. The expected collaborator is scoring-only and cannot gate requests. +Each task permits one resolver and one neighbor lookup after the community. + +The [complete review](../../benchmarks/agent_query/community_identity_navigation_review_panel_a.json) +records every task and the provenance of its selected ID. + +| Measure | Compass | Graphify | +| --- | ---: | ---: | +| Correct returned seed ID | 15/15 | 15/15 | +| Completed neighbor lookup with that ID | 15/15 | 15/15 | +| Reviewed outgoing collaborator label present | 13/14 | 14/14 | +| Unambiguous target label after validated seed lookup | 10/14 | 11/14 | + +This is meaningful counterevidence to treating the earlier label-only failures +as inability to navigate. Existing public APIs can resolve all selected seeds +with the additional source-assisted step. No product code changed in this arm, +so the stronger results are not attributed to an extraction improvement. + +A completed lookup here means the returned ID matches the frozen declaration, +the request uses that ID, and the response has the expected successful heading. +It does not mean the legacy neighbor body explicitly reports its seed ID. +Likewise, Click's `close`, jsoup's `parseFragment`, and jsoup's `isWhitespace` +labels each map to multiple target declarations in both graphs. Their displayed +labels are not counted as unique destination identities. The earlier name-only +identity audit remains separately recorded; these different metrics must not +be silently substituted for one another. + +The only reviewed direct pair absent from the frozen Compass graph is still +WalkDir `push -> DirList.close` at `src/lib.rs:906`. Graphify stores and displays +it. All five earlier split-community pairs now expose the collaborator label +(two Compass, three Graphify); these unequal subsets remain descriptive. +All community memberships and displayed neighbor multiplicities agree with +their stored graphs. That does not establish precision for every extra edge. + +All **90 public tool calls succeed** with no timeout or cap failure. Compass +call text totals 156,976 bytes and response-wire totals 694,354; Graphify totals +55,402 and 60,410. Including initialization, listing, requests and stderr, the +session totals are **785,989 versus 102,213 bytes**. These are real costs of this +fixed workflow. Compass search supplies multiple candidates, source anchors and +richer structured evidence, while Graphify's lookup returns one node. Native +limits and semantic payloads differ; these totals are not a universal efficiency +ranking or an equal-token result. + +All **129 developer-harness tests** pass. Eight new resolver tests cover exact +anchors, collisions, truncation, schema errors and returned-ID provenance. A +missing-target-oracle regression ensures seed navigation can still be scored +when no expected destination ID is available. Final recapture after removing +target-oracle availability from workflow control flow retains all 90 response +packets and 30 task audits identically. A separate same-agent verifier checks +raw requests/responses, graph hashes, source-coordinate identity and adjacency; +it is not an independent semantic reviewer. Rust tests were not rerun because +this iteration changes only evaluation code/docs and reuses the validated binary. + +Next work should address the missing Rust indexed-receiver call, exact +destination identities in public call/navigation output, and bounded resolver +cost. Natural-language seed discovery, broader source precision, god-object +responsibility evidence, longer directed walks and held-out confirmation remain +open; the supplied declaration coordinates make this a different task from +unassisted discovery. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 6c70df5e91f1971552db1ca68df66bf89f78a680 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 01:25:06 -0700 Subject: [PATCH 56/97] audit: freeze indexed receiver development evaluation --- ...dex_receiver_development_registration.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 benchmarks/agent_query/rust_index_receiver_development_registration.json diff --git a/benchmarks/agent_query/rust_index_receiver_development_registration.json b/benchmarks/agent_query/rust_index_receiver_development_registration.json new file mode 100644 index 000000000..05cb33ec6 --- /dev/null +++ b/benchmarks/agent_query/rust_index_receiver_development_registration.json @@ -0,0 +1,19 @@ +{ + "schema": "compass.rust-index-receiver-development-registration/1", + "scope": "Development correction of the previously reviewed WalkDir IntoIter.push -> DirList.close miss. The five repositories and all questions are reused development inputs, not held-out evidence or a broad precision score.", + "baselineRun": "runs/java-varargs-panel-a-02/run.json", + "baselineRunSha256": "c2bc04366343fbd32bbdf84ccfb1db7aaad2e2ed38b38f979065c6755432d792", + "baselineNavigation": "community-identity-navigation-02/capture.json", + "baselineNavigationSha256": "50910af023897886c683fcffc9228ef894303f56659c42030c573e9b92b7a3f9", + "repositories": ["chi", "click", "jsoup", "redux", "walkdir"], + "buildPolicy": "Rebuild all five Compass graphs from the exact baseline source commits with the same native-only extract arguments, into fresh external output directories. Retain the frozen Graphify graphs byte-for-byte; this arm does not compare extraction timing. Check every source checkout before and after. Preserve all old graphs.", + "graphReview": "Compare all added/removed Compass call edges by source ID, target ID, relation and source anchor, preserving multiplicity. Review every changed call edge against the pinned source, including wrong targets and deferred records. Report other node/edge changes. A recovered expected call alone is not sufficient evidence of precision.", + "navigationPolicy": "Reuse community_identity_navigation_panel_a.json and all 15 tasks from community_task_pairs_panel_a.json. Only graph/source-run/registration digest references change to identify new artifacts. Prepare start communities symmetrically; use only public returned source-matched IDs for requests. Capture both tools again with unchanged limits and all failures. Compare all 14 direct-call tasks; keep label presence, exact seed identity and unambiguous target labels separate.", + "validation": "Run focused Rust producer and universal resolver regressions, repository Rust baseline, product boundary and product tests, and code-graph fixture qualification. Preserve source-file hashes and complete command logs for the tested implementation.", + "limitations": [ + "Same-agent source review is not independent evaluation.", + "Improving one known development miss cannot establish overall superiority to Graphify.", + "Graph consistency is not source precision; broad unreviewed edges remain unscored.", + "Natural-language discovery, god-object responsibility judgments, longer directed walks and held-out confirmation remain separate requirements." + ] +} From 18bb37ea312bc23a3a677538ac03906c73a8ade4 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 01:55:00 -0700 Subject: [PATCH 57/97] fix: resolve Rust indexed receivers from source types --- CHANGELOG.md | 7 + COMPATIBILITY.md | 22 ++ crates/compass-files/src/cache.rs | 2 +- .../compass-languages/src/evidence/build.rs | 320 +++++++++++++++++- .../tests/rust_index_receivers.rs | 225 ++++++++++++ .../tests/universal_resolution/rust.rs | 59 ++++ 6 files changed, 619 insertions(+), 16 deletions(-) create mode 100644 crates/compass-languages/tests/rust_index_receivers.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b23f39a..cc11008cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +- Resolve Rust method receivers reached through source-proven scalar indexes + into standard vectors, arrays, and slices, retaining field declaration scope + and each call occurrence. Preserve intermediate modules in qualified Rust + type paths instead of selecting a same-named outer type. Custom containers, ranges, unknown index types, + and shadowed collection names remain unresolved. Rebuild graphs to refresh + older AST caches. + - Prefer exact IDs and symbol names for MCP neighbor navigation before broader prefix/substring matches, while preserving genuine declaration ambiguity and fuzzy fallback when no exact candidate exists. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 4a8d4d64e..387c3e843 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -31,6 +31,28 @@ not maintain command-specific fallbacks for older releases. Compass 0.3.0 itself remains supported. The extension adapts typed call-query results for the known nested-anchor limitation in that stable release. +## Rust indexed method receivers + +Rust call extraction follows bounded field and scalar-index receiver syntax +when source types establish a standard `Vec`, array, or slice and a decimal integer +literal or `usize` index. Supported reference and standard `Box`/`Rc`/`Arc` +wrappers preserve the element type. Field types retain their declaration scope, +and repeated calls retain distinct source anchors. Type-path qualification +retains every intermediate module when expanding a local or imported alias. +Explicit standard-vector +and element imports may be resolved by existing import evidence. + +Custom `Index` implementations, range indexes, unknown index types, raw +pointers, ambiguous or shadowed type names, and unsupported expressions do not +establish an element-method target. Prelude-vector inference is disabled by +visible wildcard imports or source attributes that may disable the standard +prelude. Generic field substitution and cross-file field layout discovery are +not compiler inference capabilities of this rule. + +Disposable AST cache semantics advance from 6 to 7 for these extraction facts. +Rebuild a graph to obtain them; published history, evidence schema, producer +capabilities, graph schema, and package version remain unchanged. + ## Compatibility evidence Compass changes are verified with native evidence: diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index b5b3f5744..e2202f1d9 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "6"; +pub const AST_CACHE_VERSION: &str = "7"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index a625e45fb..531b86bbe 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -976,6 +976,15 @@ struct RustValueTypeVersion { shadows_alias: bool, } +// Preserve the declaration context when a receiver crosses a field boundary. +// A field's element type is resolved in its defining module, not the caller's. +#[derive(Clone)] +struct RustSourceType { + raw: String, + owner: DeclarationContext, + source_start: usize, +} + #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] enum RustPlatformCfg { Fallback, @@ -1022,7 +1031,9 @@ struct DirectEvidenceState<'source> { rust_imported_typed_receivers: HashSet<(String, String)>, rust_platform_reexport_bindings: HashMap, rust_platform_fallbacks: HashSet<(String, String)>, - rust_field_types: HashMap>, + rust_field_types: HashMap>, + rust_standard_prelude_enabled: bool, + rust_ambiguous_field_types: HashSet<(String, String)>, rust_value_types: HashMap>>, rust_callable_return_types: HashMap>, rust_call_result_bindings: HashMap<(String, String, usize), String>, @@ -1114,6 +1125,9 @@ impl<'source> DirectEvidenceState<'source> { rust_platform_reexport_bindings: HashMap::new(), rust_platform_fallbacks: HashSet::new(), rust_field_types: HashMap::new(), + rust_ambiguous_field_types: HashSet::new(), + rust_standard_prelude_enabled: pipeline.producer.language == "rust" + && rust_standard_prelude_enabled(root, source), rust_value_types: HashMap::new(), rust_callable_return_types: HashMap::new(), rust_call_result_bindings: HashMap::new(), @@ -5005,6 +5019,160 @@ impl<'source> DirectEvidenceState<'source> { scope_id } + fn rust_indexed_type_name(&self, receiver: &RustSourceType) -> Option { + let nominal = rust_nominal_type_path(rust_indexable_type(&receiver.raw)?)?; + if self + .visible_import_binding_is_ambiguous(&receiver.owner, qualified_binding_head(&nominal)) + { + return None; + } + rust_qualify_evidence_path(self, &receiver.owner, &nominal, receiver.source_start) + } + + // Only called for receiver syntax containing an index. Unsupported forms + // stay unresolved instead of falling back to the collection's method. + fn rust_indexed_receiver_type( + &self, + owner: &DeclarationContext, + node: Node<'_>, + budget: usize, + ) -> Option { + let budget = budget.checked_sub(1)?; + if self.overlaps_parser_error(node) { + return None; + } + match node.kind() { + "self" => Some(RustSourceType { + raw: rust_callable_owner(owner)?.to_owned(), + owner: owner.clone(), + source_start: node.start_byte(), + }), + "identifier" => Some(RustSourceType { + raw: self + .rust_value_type_for(owner, &self.text(node), node.start_byte(), Some(node))? + .to_owned(), + owner: owner.clone(), + source_start: node.start_byte(), + }), + "parenthesized_expression" => { + self.rust_indexed_receiver_type(owner, node.named_child(0)?, budget) + } + "reference_expression" => { + let value = node.child_by_field_name("value")?; + let mut receiver = self.rust_indexed_receiver_type(owner, value, budget)?; + // Method/field receivers may auto-dereference this reference, + // but an index operand such as &n is not a scalar usize. + receiver.raw = format!("&{}", receiver.raw); + Some(receiver) + } + "field_expression" => { + let mut receiver = self.rust_indexed_receiver_type( + owner, + node.child_by_field_name("value")?, + budget, + )?; + let field = self.text(node.child_by_field_name("field")?); + for _ in 0..16 { + let raw = rust_indexable_type(&receiver.raw)?; + let qualified = self.rust_indexed_type_name(&receiver)?; + if self + .rust_ambiguous_field_types + .contains(&(qualified.clone(), field.clone())) + { + return None; + } + if let Some(field_type) = self + .rust_field_types + .get(&qualified) + .and_then(|fields| fields.get(&field)) + { + return Some(field_type.clone()); + } + if !rust_source_proven_deref_wrapper(&qualified) { + return None; + } + receiver.raw = rust_single_generic_type_argument(raw)?.to_owned(); + } + None + } + "index_expression" => { + let container = node.named_child(0)?; + let index = node.named_child(1)?; + if !self.rust_scalar_index(owner, index, budget) { + return None; + } + let mut receiver = self.rust_indexed_receiver_type(owner, container, budget)?; + for _ in 0..16 { + let raw = rust_indexable_type(&receiver.raw)?; + if let Some(element) = rust_array_or_slice_element(raw) { + receiver.raw = element.to_owned(); + return Some(receiver); + } + let nominal = rust_nominal_type_path(raw)?; + let context = &receiver.owner; + let position = receiver.source_start; + let qualified = self.rust_indexed_type_name(&receiver)?; + let standard_vec = + matches!(qualified.as_str(), "std::vec::Vec" | "alloc::vec::Vec") + || (nominal == "Vec" + && self.rust_standard_prelude_enabled + && self.local_target_for(context, "Vec").is_none() + && self + .imported_target_for_occurrence(context, "Vec", position, true) + .is_none() + && self + .import_binding_version_at(context, "*", position, true) + .is_none() + && !self.visible_import_binding_is_ambiguous(context, "*")); + if standard_vec { + receiver.raw = rust_single_generic_type_argument(raw)?.to_owned(); + return Some(receiver); + } + if !rust_source_proven_deref_wrapper(&qualified) { + return None; + } + receiver.raw = rust_single_generic_type_argument(raw)?.to_owned(); + } + None + } + _ => None, + } + } + + fn rust_scalar_index(&self, owner: &DeclarationContext, node: Node<'_>, budget: usize) -> bool { + if budget == 0 { + return false; + } + if node.kind() == "integer_literal" { + // Unsuffixed literals acquire usize from built-in sequence indexing. + // Keep suffixed non-usize integers and arbitrary index expressions unresolved. + let raw = self.text(node); + let raw = raw.strip_suffix("usize").unwrap_or(&raw); + return !raw.is_empty() + && raw + .bytes() + .all(|byte| byte.is_ascii_digit() || byte == b'_'); + } + self.rust_indexed_receiver_type(owner, node, budget) + .is_some_and(|index| { + index.raw.trim() == "usize" + && self.local_target_for(&index.owner, "usize").is_none() + && self + .imported_target_for_occurrence( + &index.owner, + "usize", + index.source_start, + true, + ) + .is_none() + && self + .import_binding_version_at(&index.owner, "*", index.source_start, true) + .is_none() + && !self.visible_import_binding_is_ambiguous(&index.owner, "usize") + && !self.visible_import_binding_is_ambiguous(&index.owner, "*") + }) + } + fn rust_field_receiver_type( &self, owner: &DeclarationContext, @@ -5072,7 +5240,7 @@ impl<'source> DirectEvidenceState<'source> { .get(&qualified) .and_then(|fields| fields.get(field)) { - return Some(field_type.clone()); + return Some(field_type.raw.clone()); } if !rust_source_proven_deref_wrapper(&qualified) { return None; @@ -5345,10 +5513,25 @@ impl<'source> DirectEvidenceState<'source> { if name.is_empty() || raw.is_empty() { return; } - self.rust_field_types + let fields = self + .rust_field_types .entry(owner.qualified_name.clone()) - .or_default() - .insert(name, raw); + .or_default(); + if fields + .get(&name) + .is_some_and(|previous| previous.source_start != type_node.start_byte()) + { + self.rust_ambiguous_field_types + .insert((owner.qualified_name.clone(), name.clone())); + } + fields.insert( + name, + RustSourceType { + raw, + owner: owner.clone(), + source_start: type_node.start_byte(), + }, + ); } fn add_rust_enum_members( @@ -5772,7 +5955,15 @@ impl<'source> DirectEvidenceState<'source> { && self .rust_value_binding_for(owner, binding_name, function.start_byte(), Some(function)) .is_some_and(|version| version.shadows_alias); - let direct_binding = (platform_reexport_bindings.is_none() && !shadows_alias) + let indexed_receiver = function.kind() == "field_expression" + && function + .child_by_field_name("value") + .is_some_and(rust_receiver_contains_index); + // A binding for the container/root object cannot select the indexed + // element's method, including when element-type inference fails. + let direct_binding = (platform_reexport_bindings.is_none() + && !shadows_alias + && !indexed_receiver) .then(|| { if uses_type_namespace { self.import_binding_version_at(owner, binding_name, function.start_byte(), true) @@ -5791,10 +5982,11 @@ impl<'source> DirectEvidenceState<'source> { .next() .is_some_and(char::is_uppercase) }); - let wildcard_binding = (direct_binding.is_none() && wildcard_lookup_eligible) - .then(|| self.rust_wildcard_binding(owner, function.start_byte())) - .flatten() - .cloned(); + let wildcard_binding = + (direct_binding.is_none() && wildcard_lookup_eligible && !indexed_receiver) + .then(|| self.rust_wildcard_binding(owner, function.start_byte())) + .flatten() + .cloned(); let fallback_binding = direct_binding.clone().or_else(|| wildcard_binding.clone()); let call_result_binding = if platform_reexport_bindings.is_none() { self.rust_call_result_binding_for_occurrence( @@ -5945,6 +6137,16 @@ impl<'source> DirectEvidenceState<'source> { .imported_target_for_occurrence(owner, spelling, 0, true) .cloned(); }; + if use_node.kind() == "field_expression" + && let Some(receiver) = use_node.child_by_field_name("value") + && rust_receiver_contains_index(receiver) + { + let receiver = self.rust_indexed_receiver_type(owner, receiver, 32)?; + let qualified = self.rust_indexed_type_name(&receiver)?; + return self + .rust_receiver_method_target(&qualified, spelling) + .or_else(|| Some(rust_join_qualified(&qualified, spelling))); + } let normalized_qualifier = rust_normalize_path(raw_qualifier); if let Some(inner) = normalized_qualifier .strip_prefix('<') @@ -9503,6 +9705,92 @@ fn rust_normalize_path(raw: &str) -> String { rust_strip_generic_arguments(raw) } +fn rust_receiver_contains_index(mut node: Node<'_>) -> bool { + for _ in 0..32 { + match node.kind() { + "index_expression" => return true, + "field_expression" => { + let Some(value) = node.child_by_field_name("value") else { + return false; + }; + node = value; + } + "parenthesized_expression" | "reference_expression" | "unary_expression" => { + let Some(value) = node + .child_by_field_name("value") + .or_else(|| node.named_child(0)) + else { + return false; + }; + node = value; + } + _ => return false, + } + } + // Don't fall back to textual receiver inference when the bound is reached. + true +} + +fn rust_standard_prelude_enabled(root: Node<'_>, source: &[u8]) -> bool { + let mut cursor = root.walk(); + loop { + let node = cursor.node(); + if matches!(node.kind(), "attribute_item" | "inner_attribute_item") { + let text = source.get(node.byte_range()).unwrap_or_default(); + // Conservatively reject conditional or nested prelude changes too. + if [b"no_implicit_prelude".as_slice(), b"no_std", b"no_core"] + .iter() + .any(|name| text.windows(name.len()).any(|part| part == *name)) + { + return false; + } + } + if cursor.goto_first_child() { + continue; + } + loop { + if cursor.goto_next_sibling() { + break; + } + if !cursor.goto_parent() { + return true; + } + } + } +} + +// Built-in indexing auto-dereferences references, never raw pointers. +fn rust_indexable_type(mut raw: &str) -> Option<&str> { + for _ in 0..16 { + raw = raw.trim(); + if let Some(rest) = raw.strip_prefix('&') { + raw = rest.trim_start(); + if let Some(lifetime) = raw.strip_prefix('\'') { + let end = lifetime.find(char::is_whitespace)?; + raw = lifetime[end..].trim_start(); + } + raw = raw.strip_prefix("mut ").unwrap_or(raw); + } else { + return (!raw.is_empty() && !raw.starts_with('*')).then_some(raw); + } + } + None +} + +fn rust_array_or_slice_element(raw: &str) -> Option<&str> { + let inner = raw.strip_prefix('[')?.strip_suffix(']')?.trim(); + let mut depth = 0_u16; + for (offset, character) in inner.char_indices() { + match character { + '[' | '(' | '<' => depth = depth.checked_add(1)?, + ']' | ')' | '>' => depth = depth.checked_sub(1)?, + ';' if depth == 0 => return Some(inner.get(..offset)?.trim()), + _ => {} + } + } + (!inner.is_empty() && depth == 0).then_some(inner) +} + fn rust_nominal_type_path(raw: &str) -> Option { let mut raw = raw.trim(); loop { @@ -10027,18 +10315,20 @@ fn rust_qualify_evidence_path( .rust_associated_type_for(owner, spelling) .map(|associated_type| associated_type.qualified_name.clone()); } - let binding_name = qualifier.map(qualified_binding_head).unwrap_or(spelling); + // Keep the entire suffix when expanding a module/type alias. Taking only + // the terminal spelling would turn api::nested::Entry into api::Entry. + let (binding_name, suffix) = split_qualified_head(&raw); if let Some(target) = state.imported_target_for_occurrence(owner, binding_name, use_start, true) { - return Some(if qualifier.is_some() { - rust_join_qualified(target, spelling) + return Some(if let Some(suffix) = suffix { + rust_join_qualified(target, suffix) } else { target.clone() }); } if let Some(target) = state.local_target_for(owner, binding_name) { - return Some(if qualifier.is_some() { - rust_join_qualified(target, spelling) + return Some(if let Some(suffix) = suffix { + rust_join_qualified(target, suffix) } else { target.clone() }); diff --git a/crates/compass-languages/tests/rust_index_receivers.rs b/crates/compass-languages/tests/rust_index_receivers.rs new file mode 100644 index 000000000..fa4eed659 --- /dev/null +++ b/crates/compass-languages/tests/rust_index_receivers.rs @@ -0,0 +1,225 @@ +use std::error::Error; +use std::path::Path; + +use compass_languages::{CandidateRelation, Engine, SemanticRole}; + +#[test] +fn indexed_field_calls_preserve_element_owner_and_each_occurrence() -> Result<(), Box> { + let source = b"struct Entry;\nimpl Entry { fn close(&mut self) {} }\nstruct Walker { entries: Vec, cursor: usize }\nimpl Walker { fn close(&mut self) {} fn advance(&mut self) {\n self.entries[self.cursor].close();\n self.entries[0].close();\n} }\n"; + let extraction = Engine::default().extract_source(Path::new("src/lib.rs"), source)?; + let evidence = extraction + .semantic_evidence + .ok_or("missing Rust evidence")?; + let calls = evidence + .candidates + .iter() + .filter(|candidate| { + candidate.relation == CandidateRelation::Calls && candidate.target_spelling == "close" + }) + .collect::>(); + assert_eq!(calls.len(), 2); + for call in calls { + assert_eq!( + call.constraints.qualified_name.as_deref(), + Some("crate::Entry::close"), + "{call:#?}" + ); + assert!(!call.constraints.allow_external); + let occurrence = evidence + .occurrences + .iter() + .find(|o| Some(&o.id) == call.occurrence_id.as_ref()) + .ok_or("missing occurrence")?; + assert_eq!(occurrence.role, SemanticRole::Call); + let raw = std::str::from_utf8( + &source[usize::try_from(occurrence.range.start_byte)? + ..usize::try_from(occurrence.range.end_byte)?], + )?; + assert!( + matches!( + raw, + "self.entries[self.cursor].close" | "self.entries[0].close" + ), + "{raw}" + ); + } + Ok(()) +} + +fn close_targets(source: &str) -> Result>, Box> { + let evidence = Engine::default() + .extract_source(Path::new("src/lib.rs"), source.as_bytes())? + .semantic_evidence + .ok_or("missing Rust evidence")?; + Ok(evidence + .candidates + .iter() + .filter(|call| call.relation == CandidateRelation::Calls && call.target_spelling == "close") + .map(|call| call.constraints.qualified_name.clone()) + .collect()) +} + +#[test] +fn standard_sequences_and_nested_field_indexes_retain_the_element_type() +-> Result<(), Box> { + for (ty, expression) in [ + ("Vec", "entries[0]"), + ("std::vec::Vec", "entries[0usize]"), + ("alloc::vec::Vec", "entries[0]"), + ("&[Entry]", "entries[0]"), + ("&mut [Entry; 4]", "entries[0]"), + ("[[Entry; 2]; 4]", "entries[0][1]"), + ("Vec>", "entries[0][1]"), + ("Box<[Entry]>", "entries[0]"), + ("std::sync::Arc>", "entries[0]"), + ("Vec", "(entries[0])"), + ("Vec", "(&entries[0])"), + ("Vec", "(&mut entries[0])"), + ] { + let source = format!( + "struct Entry; impl Entry {{ fn close(&self) {{}} }} fn run(mut entries: {ty}) {{ {expression}.close(); }}" + ); + assert_eq!( + close_targets(&source)?, + vec![Some("crate::Entry::close".to_owned())], + "{source}" + ); + } + let source = "struct Entry; impl Entry { fn close(&self) {} } struct Slot { entry: Entry } struct Store { slots: Vec } fn run(store: &Store, n: usize) { store.slots[n].entry.close(); }"; + assert_eq!( + close_targets(source)?, + vec![Some("crate::Entry::close".to_owned())] + ); + Ok(()) +} + +#[test] +fn indexed_field_type_names_use_the_field_declaration_scope() -> Result<(), Box> { + let source = "mod model { pub struct Entry; impl Entry { pub fn close(&self) {} } pub struct Store { pub entries: Vec } } struct Entry; impl Entry { fn close(&self) {} } fn run(store: &model::Store) { store.entries[0].close(); }"; + assert_eq!( + close_targets(source)?, + vec![Some("crate::model::Entry::close".to_owned())] + ); + Ok(()) +} + +#[test] +fn custom_containers_and_shadowed_vec_never_imply_the_generic_argument() +-> Result<(), Box> { + for prefix in [ + "struct Vec(T);", + "use crate::custom::Vec;", + "use crate::custom::*;", + "use crate::one::*; use crate::two::*;", + "#![no_implicit_prelude]", + "#![no_std]", + "#[cfg_attr(feature = \"bare\", no_implicit_prelude)] mod other {}", + ] { + let source = format!( + "{prefix} struct Entry; impl Entry {{ fn close(&self) {{}} }} fn run(entries: Vec) {{ entries[0].close(); }}" + ); + assert_eq!(close_targets(&source)?, vec![None], "{source}"); + } + for source in [ + "struct Entry; impl Entry { fn close(&self) {} } struct Other; struct Custom(T); impl std::ops::Index for Custom { type Output = Other; fn index(&self, _: usize) -> &Other { todo!() } } fn run(entries: Custom) { entries[0].close(); }", + "fn run(entries: Vec) { entries[0].close(); }", + "struct Holder { entries: Vec } impl Holder { fn run(&self) { self.entries[0].close(); } }", + ] { + assert_eq!(close_targets(source)?, vec![None], "{source}"); + } + Ok(()) +} + +#[test] +fn ranges_unknown_indexes_and_raw_pointers_do_not_claim_element_methods() +-> Result<(), Box> { + for (ty, index_ty, index) in [ + ("Vec", "usize", ".."), + ("Vec", "usize", "0..1"), + ("Vec", "std::ops::Range", "n"), + ("Vec", "Unknown", "n"), + ("Vec", "u32", "n"), + ("Vec", "usize", "&n"), + ("Vec", "usize", "(&n)"), + ("Vec", "&usize", "n"), + ("Vec", "usize", "0u32"), + ("*const Vec", "usize", "n"), + ("*mut [Entry; 4]", "usize", "n"), + ] { + let source = format!( + "struct Entry; impl Entry {{ fn close(&self) {{}} }} fn run(entries: {ty}, n: {index_ty}) {{ entries[{index}].close(); }}" + ); + assert_eq!(close_targets(&source)?, vec![None], "{source}"); + } + Ok(()) +} + +#[test] +fn indexed_local_receivers_and_indexes_respect_lexical_shadowing() -> Result<(), Box> { + let source = "struct Entry; impl Entry { fn close(&self) {} } fn run(entries: Vec, n: usize) { { let entries = unknown(); entries[n].close(); } { let n = unknown(); entries[n].close(); } entries[n].close(); }"; + let targets = close_targets(source)?; + assert_eq!(targets.iter().filter(|t| t.is_none()).count(), 2); + assert_eq!( + targets + .iter() + .filter(|t| t.as_deref() == Some("crate::Entry::close")) + .count(), + 1 + ); + Ok(()) +} + +#[test] +fn explicit_vec_alias_and_element_alias_are_resolved() -> Result<(), Box> { + let source = "use std::vec::Vec as Sequence; use crate::api::Entry as Item; fn run(entries: Sequence) { entries[0].close(); }"; + assert_eq!( + close_targets(source)?, + vec![Some("crate::api::Entry::close".to_owned())] + ); + Ok(()) +} + +#[test] +fn excessive_receiver_depth_remains_unresolved() -> Result<(), Box> { + let expression = format!("{}entries[0]{}", "(".repeat(40), ")".repeat(40)); + let source = format!("struct Entry; fn run(entries: Vec) {{ {expression}.close(); }}"); + assert_eq!(close_targets(&source)?, vec![None]); + Ok(()) +} + +#[test] +fn ambiguous_element_imports_do_not_select_a_target() -> Result<(), Box> { + let source = "use crate::a::Entry; use crate::b::Entry; fn run(entries: Vec) { entries[0].close(); }"; + assert_eq!(close_targets(source)?, vec![None]); + Ok(()) +} + +#[test] +fn nested_element_paths_preserve_every_module_segment() -> Result<(), Box> { + for (import, ty) in [ + ("", "api::nested::Entry"), + ("use crate::api as a;", "a::nested::Entry"), + ] { + let source = format!( + "mod api {{ pub struct Entry; impl Entry {{ pub fn close(&self) {{}} }} pub mod nested {{ pub struct Entry; impl Entry {{ pub fn close(&self) {{}} }} }} }} {import} fn run(entries: Vec<{ty}>, entry: &{ty}) {{ entries[0].close(); entry.close(); }}" + ); + assert_eq!( + close_targets(&source)?, + vec![Some("crate::api::nested::Entry::close".to_owned()); 2], + "{source}" + ); + } + let source = "use std as standard; struct Entry; impl Entry { fn close(&self) {} } fn run(entries: standard::vec::Vec) { entries[0].close(); }"; + assert_eq!( + close_targets(source)?, + vec![Some("crate::Entry::close".to_owned())] + ); + Ok(()) +} + +#[test] +fn conditional_field_layouts_do_not_choose_one_element_type() -> Result<(), Box> { + let source = "struct A; impl A { fn close(&self) {} } struct B; impl B { fn close(&self) {} } #[cfg(feature = \"a\")] struct Store { entries: Vec } #[cfg(not(feature = \"a\"))] struct Store { entries: Vec } impl Store { fn run(&self) { self.entries[0].close(); } }"; + assert_eq!(close_targets(source)?, vec![None]); + Ok(()) +} diff --git a/crates/compass-resolve/tests/universal_resolution/rust.rs b/crates/compass-resolve/tests/universal_resolution/rust.rs index 387d0af35..274332cfb 100644 --- a/crates/compass-resolve/tests/universal_resolution/rust.rs +++ b/crates/compass-resolve/tests/universal_resolution/rust.rs @@ -3223,3 +3223,62 @@ fn run(builder: &Decoy, builders: &[Actual]) { .map(|edge| edge.string("source_location")).collect::>(); assert_eq!(sites, BTreeSet::from(["L7".to_owned(), "L8".to_owned()])); } + +#[test] +fn rust_indexed_receivers_publish_exact_cross_file_calls_and_occurrences() { + let api_source = "pub struct Entry; impl Entry { pub fn close(&self) {} }"; + let caller_source = "use crate::api::Entry; struct Store { entries: Vec, cursor: usize } impl Store { fn close(&self) {} fn run(&self) { self.entries[self.cursor].close(); self.entries[0].close(); } }"; + let sources = HashMap::from([ + ("src/api.rs".to_owned(), api_source.to_owned()), + ("src/lib.rs".to_owned(), caller_source.to_owned()), + ]); + let extractions = vec![extract("src/api.rs", api_source.as_bytes()), extract("src/lib.rs", caller_source.as_bytes())]; + let resolved = compass_resolve::resolve(&extractions, &sources); + let reversed = compass_resolve::resolve(&extractions.into_iter().rev().collect::>(), &sources); + assert_eq!(universal_edges(&resolved), universal_edges(&reversed)); + let run = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::Store::run").expect("run"); + let close = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::api::Entry::close").expect("Entry.close"); + let calls = resolved.edges.iter().filter(|edge| edge.source == run.id && edge.string("relation") == "calls").collect::>(); + assert_eq!(calls.len(), 2, "{calls:#?}"); + let mut actual_ranges = Vec::new(); + for call in calls { + assert_eq!(call.target, close.id, "{call:#?}"); + assert_eq!(call.string("confidence"), "EXTRACTED"); + assert!(call.string("extractor").contains(".universal")); + assert_ne!(call.string("resolution_rule"), "deferred-receiver"); + let start = call.attributes["start_byte"].as_u64().expect("start") as usize; + let end = call.attributes["end_byte"].as_u64().expect("end") as usize; + actual_ranges.push(&caller_source[start..end]); + } + actual_ranges.sort(); + assert_eq!(actual_ranges, ["self.entries[0].close", "self.entries[self.cursor].close"]); +} + +#[test] +fn rust_unsupported_index_receivers_never_capture_same_named_methods() { + for source in [ + "struct Entry; impl Entry { fn close(&self) {} } struct Vec(T); impl Vec { fn close(&self) {} } fn run(entries: Vec) { entries[0].close(); }", + "struct Entry; impl Entry { fn close(&self) {} } struct Custom(T); struct Store { entries: Custom } impl Store { fn close(&self) {} } fn run(store: &Store) { store.entries[0].close(); }", + "struct Entry; impl Entry { fn close(&self) {} } fn run(entries: Vec, n: Unknown) { entries[n].close(); }", + "struct Entry; impl Entry { fn close(&self) {} } fn run(entries: Vec) { entries[..].close(); }", + "struct Entry; trait A { fn close(&self); } trait B { fn close(&self); } impl A for Entry { fn close(&self) {} } impl B for Entry { fn close(&self) {} } fn run(entries: Vec) { entries[0].close(); }", + ] { + let resolved = compass_resolve::resolve(&[extract("src/lib.rs", source.as_bytes())], &HashMap::from([("src/lib.rs".to_owned(), source.to_owned())])); + let run = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::run").expect("run"); + for call in resolved.edges.iter().filter(|edge| edge.source == run.id && edge.string("relation") == "calls") { + assert_eq!(call.string("resolution_rule"), "deferred-receiver", "{source}\n{call:#?}"); + } + } +} + +#[test] +fn rust_indexed_parameter_fields_never_bind_the_container_method() { + let source = "struct Entry; impl Entry { fn close(&self) {} } struct Store { entries: Vec } impl Store { fn close(&self) {} } fn run(store: &Store) { store.entries[0].close(); }"; + let resolved = compass_resolve::resolve(&[extract("src/lib.rs", source.as_bytes())], &HashMap::from([("src/lib.rs".to_owned(), source.to_owned())])); + let run = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::run").expect("run"); + let target = resolved.nodes.iter().find(|node| node.string("qualified_name") == "crate::Entry::close").expect("Entry.close"); + let calls = resolved.edges.iter().filter(|edge| edge.source == run.id && edge.string("relation") == "calls").collect::>(); + assert_eq!(calls.len(), 1, "{calls:#?}"); + assert_eq!(calls[0].target, target.id); + assert_eq!(calls[0].string("confidence"), "EXTRACTED"); +} From a07ab99126e76f50059ee631caa4d2b584488133 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 02:21:24 -0700 Subject: [PATCH 58/97] audit: verify Rust indexed receiver recovery --- benchmarks/agent_query/COVERAGE_PLAN.md | 16 + benchmarks/agent_query/README.md | 13 + ...ust_index_receiver_development_review.json | 346 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 60 +++ 4 files changed, 435 insertions(+) create mode 100644 benchmarks/agent_query/rust_index_receiver_development_review.json diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index b77f396fb..902d1f58b 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -256,3 +256,19 @@ on each side. The remaining direct-call gap is Compass's WalkDir indexed receiver call. Keep explicit destination identity, source precision and resolver output cost as separate next questions; existing APIs already overcome the member-label ambiguities with this extra lookup and supplied source coordinates. + + +### Rust indexed-receiver development correction + +`rust_index_receiver_development_registration.json` freezes the five-repository +rebuild, complete changed-call review and unchanged 15-task public workflow. +`rust_index_receiver_development_review.json` records one source-backed added +WalkDir call, with no other node/edge changes or changed community assignments. +The other four Compass graphs and all Graphify graphs are byte-identical. +Source-assisted collaborator labels improve from 13/14 to 14/14, tying +Graphify; unambiguous target labels improve from 10/14 to 11/14, also a tie. +Keep the three target-label ambiguities, supplied seed coordinates, reused +Graphify graphs, actual payload costs and development-only scope explicit. +Final unchanged-source validation passed, including the native baseline and +production fixture qualification. This does not establish overall superiority +or population precision. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 56a30914e..7eba2ceae 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -454,3 +454,16 @@ search returns more candidates and structured evidence; native bounds and semantic payloads differ. Report these actual workflow costs with that context. This is source-assisted development evidence, not natural-language discovery, comprehensive assertion precision, or held-out performance. + + +### Rust indexed-receiver correction + +See `rust_index_receiver_development_registration.json` and +`rust_index_receiver_development_review.json` for the fixed-protocol rerun on +Chi, Click, jsoup, Redux and WalkDir. One reviewed Rust call is recovered; +the 15-task source-assisted workflow now ties Graphify at 14/14 collaborator +labels and 11/14 unambiguous target labels. Other existing graph records and +all community assignments remain unchanged. Final-source validation passed, +including the native baseline and production fixture qualification. These reused +development tasks do not establish broad precision, +god-object diagnosis or overall superiority. diff --git a/benchmarks/agent_query/rust_index_receiver_development_review.json b/benchmarks/agent_query/rust_index_receiver_development_review.json new file mode 100644 index 000000000..933c24de2 --- /dev/null +++ b/benchmarks/agent_query/rust_index_receiver_development_review.json @@ -0,0 +1,346 @@ +{ + "schema": "compass.rust-index-receiver-development-review/1", + "scope": "Known development miss on the same five pinned repositories and 15 source-defined tasks. Same-agent source review, not held-out or independent evaluation.", + "protocolCommit": "6c70df5e91f1971552db1ca68df66bf89f78a680", + "implementationCommit": "18bb37ea312bc23a3a677538ac03906c73a8ade4", + "artifactRoot": "rust-index-receiver-03", + "binarySha256": "6be23e8cc3b82c60a5eca4037d94c4d3c7ef0aab656eaceb0f96e90e369a9b0c", + "version": "0.3.30", + "cacheSemantics": "AST 6 -> 7; graph/evidence schema and producer capabilities unchanged", + "artifacts": { + "run.json": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "graph-delta.json": "a28bfad40768db17134c7dc39bf7e5f68ab6c4d6754d632f11fa63ec291ae2f3", + "community-delta.json": "0374616e4c023a1adafcb7c11a1f72f3f5a3c1f15f3c28f84903700cf8a181fe", + "source-review.json": "dfd970416111e0fb295dd7c6864c935bcaed25a8475a52cbd96ea297612d0555", + "verification-details.json": "289d053528e3077f331ff9e59dc72fcac3c11a7e4ad58147815a7cae291bd4d0", + "policy-equivalence.json": "350b13cc4277968f9c8846e439ea290d8eb09af49be03df0aed5bf7e8890b5dd", + "navigation/capture.json": "636833c4a6847e1267b757c2cbf45691dad66cb38ffc33b409d02b7f9dd7e630", + "navigation/verified-summary.json": "f13c4828e3391a6befe56f8427a44bc025ee69c731b202171c62af83820592a2", + "navigation/verify_capture.py": "50534462013d240637a79093731b2d86760cfd0471661d718d8b910803d44e81", + "verify_delta.py": "4fdd2642211478c2abd7158df842b08bea88b78124ade1716c7569ef32e832ec", + "binary-manifest.json": "6aa495a2d44c726ce0583941f0ab2db53ff5cbe300f428a3d595a1326f28d71f", + "validation.json": "732c2509bc80fed76268c43e53d64ce29235b10f8f81de39b730f3709dc5bd88", + "final-validation-summary.json": "fdbf13221cee8d60954d44037f0de1c5f4f4d2a187a570468817a9234cd7b1db", + "harness-tests.log": "57825a0457d08a37a3402ce6a4f3535d3687cb458127293393ffca02b46d0a43", + "fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "rust-language-tests-final.log": "a62a7ad652853a3aeb5aaa48bbd5518f093b166964421ea5019ce1de5d71c01f", + "universal-resolver-tests-final.log": "c4d644ed23bb1c90963351466ac9049441cb5f209e4a1fa44cfcbee517a51855", + "clippy-final.log": "5bc29eb199d14541206e2200c80c7f9c3a6823924821a86ececc304018f551e1", + "workspace-tests-final.log": "cb017d306359fdcddefecc764dc19fe7ca8ad6dd76ac7d6f868a47092e3465bc", + "product-tests-final.log": "79473ac727d45d0e212f0ddebe97969a06208bc3a4d51adb0bfb1e6357ecaacc", + "product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "qualification-final.log": "25fd6f552b78d460d95d4812f3b6f63153c597c3efd938530b144e6baaf2e618" + }, + "baseline": { + "compass": { + "collaboratorLabelPresent": 13, + "unambiguousTargetLabel": 10 + }, + "graphify": { + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11 + } + }, + "summary": { + "compass": { + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11, + "callTextBytes": 157011, + "callResponseWireBytes": 694390, + "fullSessionBytesIncludingRequestsAndStderr": 786025 + }, + "graphify": { + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11, + "callTextBytes": 55402, + "callResponseWireBytes": 60410, + "fullSessionBytesIncludingRequestsAndStderr": 102213 + } + }, + "graphChanges": [ + { + "repository": "chi", + "before": { + "nodes": 729, + "edges": 1914 + }, + "after": { + "nodes": 729, + "edges": 1914 + }, + "addedRecords": 0, + "removedRecords": 0, + "addedNodes": 0, + "removedNodes": 0, + "changedExistingCallPayloads": 0 + }, + { + "repository": "click", + "before": { + "nodes": 4264, + "edges": 6387 + }, + "after": { + "nodes": 4264, + "edges": 6387 + }, + "addedRecords": 0, + "removedRecords": 0, + "addedNodes": 0, + "removedNodes": 0, + "changedExistingCallPayloads": 0 + }, + { + "repository": "jsoup", + "before": { + "nodes": 6116, + "edges": 21110 + }, + "after": { + "nodes": 6116, + "edges": 21110 + }, + "addedRecords": 0, + "removedRecords": 0, + "addedNodes": 0, + "removedNodes": 0, + "changedExistingCallPayloads": 0 + }, + { + "repository": "redux", + "before": { + "nodes": 3503, + "edges": 5653 + }, + "after": { + "nodes": 3503, + "edges": 5653 + }, + "addedRecords": 0, + "removedRecords": 0, + "addedNodes": 0, + "removedNodes": 0, + "changedExistingCallPayloads": 0 + }, + { + "repository": "walkdir", + "before": { + "nodes": 288, + "edges": 1205 + }, + "after": { + "nodes": 288, + "edges": 1206 + }, + "addedRecords": 1, + "removedRecords": 0, + "addedNodes": 0, + "removedNodes": 0, + "changedExistingCallPayloads": 0 + } + ], + "sourceJudgment": { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "sourceFile": "src/lib.rs", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "caller": "walkdir::IntoIter::push", + "callee": "walkdir::DirList::close", + "callLine": 906, + "targetDeclarationLine": 1008, + "sourceReason": "The field stack_list has Vec type and oldest_opened has usize type. The indexed receiver at line 906 denotes a DirList, whose inherent close method begins at line 1008. This is an outgoing call from IntoIter.push, independent of graph community assignment." + }, + "unchangedEvidence": { + "otherFourCompassGraphsByteIdentical": true, + "allFiveNodeArraysIdentical": true, + "allCommunityAssignmentsIdentical": true, + "all45GraphifyResponsesIdentical": true + }, + "changedCompassResponses": "One neighbor text gains the close call. Three WalkDir resolver packets change only graph identity and view digest; all other response packets are identical.", + "remainingAmbiguousTargetLabels": [ + { + "tool": "compass", + "task": "click-atomic-close", + "identities": 5 + }, + { + "tool": "graphify", + "task": "click-atomic-close", + "identities": 5 + }, + { + "tool": "compass", + "task": "jsoup-html-fragment-parse", + "identities": 5 + }, + { + "tool": "compass", + "task": "jsoup-html-whitespace", + "identities": 3 + }, + { + "tool": "graphify", + "task": "jsoup-html-fragment-parse", + "identities": 3 + }, + { + "tool": "graphify", + "task": "jsoup-html-whitespace", + "identities": 3 + } + ], + "validation": { + "status": "passed", + "finalSourceDirectory": "rust-index-receiver-03", + "sourceCommit": "18bb37ea312bc23a3a677538ac03906c73a8ade4", + "binaryMatchesFinalBuild": true, + "counts": { + "rust-language-tests": { + "passed": 38, + "failed": 0, + "ignored": 0 + }, + "universal-resolver-tests": { + "passed": 211, + "failed": 0, + "ignored": 0 + }, + "workspace-tests": { + "passed": 1101, + "failed": 0, + "ignored": 2 + }, + "product-tests": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 3.92 + }, + { + "name": "rust-language-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-languages", + "--test", + "rust_index_receivers", + "--test", + "rust_universal_conformance", + "--test", + "rust_universal_phase2", + "--locked" + ], + "exitCode": 0, + "seconds": 13.6 + }, + { + "name": "universal-resolver-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-resolve", + "--test", + "universal_resolution", + "--locked" + ], + "exitCode": 0, + "seconds": 4.7 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 20.82 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 129.45 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 2.64 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.04 + }, + { + "name": "qualification", + "argv": [ + "bash", + "scripts/qualify_code_graph_v1.sh", + "--fixtures-only" + ], + "exitCode": 0, + "seconds": 168.82 + } + ], + "harnessTestsPassed": 129 + }, + "interpretation": "Compass recovers the one missing reviewed collaborator, tying Graphify at 14/14 displayed collaborators and 11/14 globally unambiguous target labels. This is not overall superiority or a broad source-precision estimate.", + "limitations": [ + "Graphify graph artifacts are reused byte-for-byte; public MCP calls are recaptured. Extraction timing is not compared.", + "Exact seed source coordinates are supplied to both tools. Starting communities are prepared symmetrically and not scored as retrieval.", + "Three target display labels remain ambiguous for both tools. A successful lookup with a verified seed ID is distinct from self-identifying neighbor output.", + "Only the changed source call is newly source-reviewed; existing graph assertions remain outside this delta review.", + "Native payloads differ in richness and controls. Actual byte costs are reported, not equal-token efficiency.", + "God-object responsibility evidence, broader source precision, longer directed walks and fresh held-out confirmation remain open." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index cc7002e23..17d6088ea 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2212,6 +2212,66 @@ responsibility evidence, longer directed walks and held-out confirmation remain open; the supplied declaration coordinates make this a different task from unassisted discovery. +## Rust indexed-receiver recovery + +The development protocol is frozen in +`benchmarks/agent_query/rust_index_receiver_development_registration.json` +(commit `6c70df5e`). Implementation `18bb37ea` follows bounded Rust receiver +syntax through source-proven scalar indexes into standard vectors, arrays and +slices. Field types retain their declaration context; nested qualified names +retain every module segment. Ambiguous imports/layouts, custom containers, +ranges, unknown index types, raw pointers and root-container method fallbacks +cannot establish an element-method target. AST cache semantics advance to 7; +the package stays at 0.3.30 and published graph schemas/history remain unchanged. + +All five Compass graphs were rebuilt from the same pinned source commits with +the original native-only arguments into fresh outputs. The frozen Graphify +graphs were retained byte-for-byte; both public MCP workflows were recaptured. +This arm does not compare extraction timing. The review and artifact hashes are +in `benchmarks/agent_query/rust_index_receiver_development_review.json`; +complete logs, graphs and transcripts are under `rust-index-receiver-03`. + +The complete graph comparison finds **one added call and no removed or changed +existing records**: WalkDir `IntoIter::push` calls `DirList::close` at +`src/lib.rs:906`. Source establishes `stack_list: Vec` and +`oldest_opened: usize`; the target method begins at line 1008. A separate +same-agent verifier checks the full graph-record delta, source bytes, caller +range and target identity. All five node arrays and community assignments are +unchanged; the other four Compass graphs are byte-identical. This is evidence +for the changed call, not precision for every existing graph assertion. + +The original 15 tasks, source witnesses, selectors and limits remain unchanged: + +| Source-assisted navigation measure | Compass before | Compass after | Graphify | +| --- | ---: | ---: | ---: | +| Correct seed ID and completed lookup | 15/15 | 15/15 | 15/15 | +| Reviewed outgoing collaborator label | 13/14 | 14/14 | 14/14 | +| Globally unambiguous target label | 10/14 | 11/14 | 11/14 | + +All 90 public calls succeed. All 45 Graphify response packets are identical to +the prior capture. Compass changes one neighbor text to include `close`; three +WalkDir resolver packets change only graph identity/view digests. The other +41 packets are identical. Compass totals 157,011 call-text bytes, 694,390 wire +response bytes and 786,025 full-session bytes; Graphify remains at 55,402, +60,410 and 102,213. Native payloads and controls differ, so these are actual +workflow costs rather than an equal-token efficiency ranking. + +Three target labels remain ambiguous for both tools: Click `close`, jsoup +`parseFragment`, and jsoup `isWhitespace`. The fix closes a known extraction +gap and produces a tie on this development panel. It does not establish overall +superiority, god-object diagnosis, broad source precision or held-out quality. +Explicit destination identities, source-grounded explanations and longer +source-verified directed walks remain next work. + +Final validation against unchanged implementation `18bb37ea` passed: formatting, +38 Rust language tests, 211 universal resolver tests, workspace Clippy, 1,101 +workspace tests (2 ignored), 9 product tests, the product-boundary check and the +complete production fixture qualification. The Python benchmark harness passed +129 tests. Source hashes remained unchanged before and after each native gate; +the frozen evaluated binary is byte-identical to the final build. Commands, +counts and log hashes are recorded in the review artifact. Broader real-repository +qualification and fresh held-out evaluation were not run in this development arm. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From bde19bb3687b8bcf8fb09fa0e9ef44c2bf87245c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 02:23:41 -0700 Subject: [PATCH 59/97] audit: freeze neighbor identity development protocol --- ...bor_identity_development_registration.json | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 benchmarks/agent_query/neighbor_identity_development_registration.json diff --git a/benchmarks/agent_query/neighbor_identity_development_registration.json b/benchmarks/agent_query/neighbor_identity_development_registration.json new file mode 100644 index 000000000..28c23b2b5 --- /dev/null +++ b/benchmarks/agent_query/neighbor_identity_development_registration.json @@ -0,0 +1,51 @@ +{ + "schema": "compass.neighbor-identity-development-registration/1", + "scope": "Reused five-repository development panel; source-assisted navigation. Same-agent review. Not held-out or overall superiority.", + "baselineImplementation": "a07ab99126e76f50059ee631caa4d2b584488133", + "baselineRoot": "rust-index-receiver-03", + "inputs": { + "run.json": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "community_task_pairs_panel_a.json": "678419fbd9fd9dddb6fffdaf108047f23f055b8c4df40035236c8ac1f561d9af", + "community_identity_navigation_panel_a.json": "114bfc837847bccee24caa9de6ac4905d4c353146b82a16633147913ccab5e23", + "navigation/capture.json": "636833c4a6847e1267b757c2cbf45691dad66cb38ffc33b409d02b7f9dd7e630" + }, + "repositories": [ + "chi", + "click", + "jsoup", + "redux", + "walkdir" + ], + "artifactPolicy": "Reuse all ten frozen graphs byte-for-byte. No extraction changes or timing comparison. Verify source commits, graph hashes, binary and Graphify environment before and after capture.", + "workflow": "Repeat all 15 registered source-assisted community -> seed resolver -> call-neighbor workflows with the existing selectors and bounds. Request construction cannot consult graph IDs or graph adjacency. Preserve all failures and count all 14 direct-call tasks.", + "metrics": { + "legacy": "Keep collaborator label presence and globally unambiguous target-label counts separate and unchanged.", + "directResponseIdentity": "Count a reviewed outgoing collaborator only if its exact node ID and an outgoing relationship from the observed seed are explicitly present in the public neighbor response. Validate returned identities, direction, anchors and all retained relationship records against frozen graphs after requests. Do not infer exact IDs from labels or conveniently matching adjacency.", + "followupIdentity": "Allow each tool one additional public resolver call per direct-call task, using only returned collaborator labels and the same registered target source file/symbol/start line available to both tools. Use the existing per-tool public resolver and source-anchor selector. Count only if the expected collaborator label was actually returned, resolver emits exactly one matching ID, and the ID matches the source oracle. This measures a source-assisted follow-up, not direct response identity.", + "cost": "Report call counts, text bytes, wire-response bytes, full-session bytes, errors/timeouts/limits, and richer payload differences. Never interpret bytes as equal semantic content or universal efficiency." + }, + "bounds": { + "requestTimeoutSeconds": 60, + "maxResponseBytes": 1048576, + "maxSessionBytes": 67108864, + "graphifyTokenBudget": 262144, + "compassResolver": { + "max_candidates": 256, + "max_nodes": 500, + "max_response_bytes": 524288 + } + }, + "implementationRequirements": [ + "Exact destination IDs and available definition anchors; no guessed IDs or source coordinates.", + "Deterministic relation records preserve direction, multiplicity, anchors and provenance, including same-label destinations, parallel relations and self loops.", + "Explicit bounded failure must not appear as empty or complete adjacency.", + "Legacy text remains available; machine consumers use a versioned structured result." + ], + "validation": "Focused query and MCP unit/integration contracts, adversarial scoring tests, native Rust baseline, product tests/boundary and applicable code graph fixture gate. Preserve full commands/logs.", + "limitations": [ + "Known development tasks; no held-out claim.", + "Graph consistency is not independent source precision.", + "Existing source witnesses cover 14 direct collaborators; other graph assertions are not newly source-scored.", + "God-object responsibility judgments, richer source explanations and longer directed walks remain separate outstanding requirements." + ] +} From d51698dc1cfb6fc7cf069926cb39964359859447 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 02:40:56 -0700 Subject: [PATCH 60/97] feat: retain exact neighbor identities and relationship evidence --- CHANGELOG.md | 4 + COMPATIBILITY.md | 19 +- MIGRATION.md | 9 + crates/compass-mcp/src/lib.rs | 185 +++++++++--- crates/compass-mcp/tests/coverage_paths.rs | 18 ++ crates/compass-query/src/lib.rs | 5 + crates/compass-query/src/neighbors.rs | 335 +++++++++++++++++++++ docs/reference/outputs.md | 31 +- 8 files changed, 556 insertions(+), 50 deletions(-) create mode 100644 crates/compass-query/src/neighbors.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index cc11008cf..a325e661d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Add exact destination identities and full relationship records to MCP neighbor + results, preserving source anchors, provenance, direction and parallel calls. + Bound adjacency work and response size; report exhaustion explicitly. + - Resolve Rust method receivers reached through source-proven scalar indexes into standard vectors, arrays, and slices, retaining field declaration scope and each call occurrence. Preserve intermediate modules in qualified Rust diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 387c3e843..c5d2abf09 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -182,8 +182,23 @@ retain their case. The tool's input schema and the MCP result envelope are unchanged. This corrects unnecessarily ambiguous lookups; no migration is needed. Relationship filters apply before repeated neighbors are grouped, so a stored call remains visible when a containment/reference edge precedes it. The tool -continues to return distinct neighbors; typed call-query tools carry occurrence -and source-site detail. +continues to display distinct neighbors. Successful results now add the versioned +`compass.query.neighbors/1` structured result inside the existing MCP transport +envelope. Each direction/neighbor group carries its exact node record and all +matching relationship records, including IDs, source sites, provenance and +parallel occurrences. Missing legacy edge IDs remain absent. Directions describe +stored endpoints; `graphDirected` preserves the artifact metadata, so an +undirected artifact does not become proof of a directed call. Text adds escaped +ID/source/location lines; the displayed relation represents the first canonical +record in the group. Use structured records for all relations and occurrences. + +Neighbor lookup reads one full snapshot to avoid losing fields in the compact +traversal cache. It is bounded by 1,000,000 examined adjacency entries, 10,000 +matching incident records, a 4,096-byte filter and a 1 MiB structured result. +Exhaustion is an explicit error, never an empty or silently partial success. +Groups sort by outgoing/incoming direction then exact ID; records sort by +canonical JSON. Self loops appear in both directions and count once against +the incident-record budget. Published graphs and historical artifacts are unchanged. ### Bounded node trails diff --git a/MIGRATION.md b/MIGRATION.md index b7ef8c0f8..5dec9cc24 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,15 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +MCP neighbor responses now include exact destination records in +`structuredContent.result` with schema `compass.query.neighbors/1`. Consumers +that need machine identities should read those records instead of parsing text +labels. The text adds identity/source lines and orders groups deterministically. +If a large neighbor request exceeds the new adjacency, record or 1 MiB semantic +response limit, it fails explicitly; narrow `relation_filter` or use the bounded +typed caller/callee queries. An empty successful result means no matching +records. No graph rebuild is required for this output change. + Rebuild Java graphs to receive corrected varargs signatures, array argument types, and overload targets. Spread parameters now retain their declared array type; calls may gain targets or select a different, source-supported overload. diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index 8551c210b..ade085169 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -5,7 +5,7 @@ mod transport; pub use transport::{HttpOptions, serve_http, serve_stdio, serve_stdio_configured}; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::fs; use std::fs::OpenOptions; use std::io::{Read as _, Write as _}; @@ -44,8 +44,8 @@ use compass_prs::{ fetch_prs, fetch_worktrees, format_prs_text, parse_ci, }; use compass_query::{ - HopPathResult, TraversalMode, find_exact_nodes, find_node, query_graph_text, sanitize_label, - shortest_hop_path, + HopPathResult, NeighborDirection, TraversalMode, direct_neighbors, find_exact_nodes, find_node, + query_graph_text, sanitize_label, shortest_hop_path, }; use rmcp::model::{ CallToolRequestParams, CallToolResult, ContentBlock, ErrorData, Implementation, @@ -372,7 +372,7 @@ impl CompassMcp { self.invoke_result(name, &mut arguments) .map(|result| { // Keep this legacy text helper stable; MCP carries both projections. - if matches!(name, "god_nodes" | "shortest_path") { + if matches!(name, "god_nodes" | "shortest_path" | "get_neighbors") { return result.text; } result @@ -587,6 +587,9 @@ impl CompassMcp { if typed_query { return invoke_typed_tool(&self.store, name, arguments, &context.path, Some(&context)); } + if name == "get_neighbors" { + return invoke_neighbor_tool(arguments, &context); + } if name == "god_nodes" { return invoke_hub_tool(arguments, &context); } @@ -1362,7 +1365,7 @@ fn tool_specs() -> Vec { ), tool( "get_neighbors", - "Get all direct neighbors of a node with edge details.", + "Get bounded direct neighbors with exact destination IDs, definition anchors and all matching relationship records. Directions describe stored endpoints; limit exhaustion fails explicitly.", json!({"type":"object","properties":{"label":{"type":"string"},"relation_filter":{"type":"string","description":"Optional: filter by relation type"}},"required":["label"]}), ), tool( @@ -2221,24 +2224,43 @@ fn tool_get_neighbors( arguments: &Map, context: &GraphContext, ) -> Result { + invoke_neighbor_tool(arguments, context) + .map(|result| result.text) + .map_err(|error| error.to_string()) +} + +fn invoke_neighbor_tool( + arguments: &Map, + context: &GraphContext, +) -> Result { + // The compact traversal cache omits identities, occurrence anchors and + // provenance. Resolve and project against one full snapshot instead. + let mut document = context.document()?; + // Retain every persisted record even for legacy non-multigraph metadata. + document.multigraph = true; + let graph = Graph::from_traversal_document(document) + .map_err(|error| InvocationError::Internal(error.to_string()))?; let query = string_argument(arguments, "label")?; let filter = optional_string(arguments, "relation_filter") .unwrap_or_default() .to_lowercase(); - let exact = find_exact_nodes(&context.graph, query); + let exact = find_exact_nodes(&graph, query); let matches = if exact.is_empty() { - find_node(&context.graph, query) + find_node(&graph, query) } else { exact }; let Some(&index) = matches.first() else { - return Ok(format!("No node matching '{query}' found.")); + return Ok(ToolInvocation { + text: format!("No node matching '{}' found.", sanitize_label(query)), + structured_content: None, + }); }; if matches.len() > 1 { const MAX_CANDIDATES: usize = 20; let mut candidates = matches .iter() - .map(|index| context.graph.node(*index)) + .map(|index| graph.node(*index)) .collect::>(); candidates.sort_by(|left, right| left.id.cmp(&right.id)); let mut lines = vec![format!( @@ -2260,53 +2282,47 @@ fn tool_get_neighbors( candidates.len() - MAX_CANDIDATES )); } - return Ok(lines.join("\n")); + return Ok(ToolInvocation { + text: lines.join("\n"), + structured_content: None, + }); } + let report = direct_neighbors(&graph, index, &filter) + .map_err(|error| InvocationError::InvalidParams(error.to_string()))?; let mut lines = vec![format!( "Neighbors of {}:", - sanitize_label(context.graph.node(index).label()) + sanitize_label(report.seed.label()) )]; - let mut outgoing = HashSet::new(); - for edge_index in context.graph.outgoing_edges(index) { - let edge = context.graph.edge(edge_index); - let Some(neighbor) = context.graph.node_index(&edge.target) else { + lines.push(format!(" seed id: {}", json!(report.seed.id))); + for group in &report.neighbors { + let Some(edge) = group.edges.first() else { continue; }; - let relation = edge.string("relation"); - if !filter.is_empty() && !relation.to_lowercase().contains(&filter) { - continue; - } - if !outgoing.insert(neighbor) { - continue; - } + let arrow = match group.direction { + NeighborDirection::Outgoing => "-->", + NeighborDirection::Incoming => "<--", + }; lines.push(format!( - " --> {} [{}] [{}]", - sanitize_label(context.graph.node(neighbor).label()), - sanitize_label(&relation), + " {arrow} {} [{}] [{}]", + sanitize_label(group.node.label()), + sanitize_label(&edge.string("relation")), sanitize_label(&edge.string("confidence")) )); - } - let mut incoming = HashSet::new(); - for edge_index in context.graph.incoming_edges(index) { - let edge = context.graph.edge(edge_index); - let Some(neighbor) = context.graph.node_index(&edge.source) else { - continue; - }; - let relation = edge.string("relation"); - if !filter.is_empty() && !relation.to_lowercase().contains(&filter) { - continue; - } - if !incoming.insert(neighbor) { - continue; - } lines.push(format!( - " <-- {} [{}] [{}]", - sanitize_label(context.graph.node(neighbor).label()), - sanitize_label(&relation), - sanitize_label(&edge.string("confidence")) + " id: {} | source: {} | location: {} | records: {}", + json!(group.node.id), + json!(group.node.source_file()), + json!(group.node.string("source_location")), + group.edges.len() )); } - Ok(lines.join("\n")) + let mut value = serde_json::to_value(&report) + .map_err(|error| InvocationError::Internal(error.to_string()))?; + value.sort_all_objects(); + Ok(ToolInvocation { + text: lines.join("\n"), + structured_content: Some(transport_envelope(value)?), + }) } fn tool_get_community( @@ -3177,6 +3193,87 @@ mod tests { Ok(()) } + #[test] + fn mcp_neighbors_expose_exact_destinations_and_full_relationship_records() + -> Result<(), Box> { + let temp = tempfile::tempdir()?; + let path = temp.path().join("neighbors.json"); + let unusual_id = "close\n\"second\""; + fs::write( + &path, + serde_json::to_vec(&json!({ + "directed":true, "multigraph":false, + "nodes":[ + {"id":"seed","label":"run()"}, + {"id":"first","label":"close()","source":{"file":"a.rs","startLine":10}}, + {"id":unusual_id,"label":"close()","source_file":"b.rs","source_location":"L20"} + ], + "links":[ + {"id":"call-1","source":"seed","target":"first","relation":"calls","relationshipSite":{"file":"caller.rs","startLine":3},"evidence":[{"origin":"ast","rule":"fixture"}]}, + {"id":"call-2","source":"seed","target":"first","relation":"calls","relationshipSite":{"file":"caller.rs","startLine":4}}, + {"source":"seed","target":unusual_id,"relation":"calls"}, + {"source":"seed","target":"first","relation":"contains"} + ] + }))?, + )?; + let server = CompassMcp::new(&path); + let result = server + .invoke_result( + "get_neighbors", + &mut json!({"label":"seed","relation_filter":"calls"}) + .as_object() + .ok_or("args")? + .clone(), + ) + .map_err(|e| e.to_string())?; + assert!(result.text.starts_with("Neighbors of run():\n")); + assert_eq!(result.text.matches("--> close() [calls]").count(), 2); + assert!(result.text.contains(&format!("id: {}", json!(unusual_id)))); + assert!(!result.text.contains(unusual_id)); + let body = result.structured_content.ok_or("missing identity")?["result"].clone(); + assert_eq!(body["schema"], "compass.query.neighbors/1"); + assert_eq!(body["seed"]["id"], "seed"); + assert_eq!(body["truncated"], false); + let rows = body["neighbors"].as_array().ok_or("neighbors")?; + assert_eq!(rows.len(), 2); + let first = rows + .iter() + .find(|r| r["node"]["id"] == "first") + .ok_or("first")?; + assert_eq!(first["direction"], "outgoing"); + assert_eq!(first["node"]["source"]["startLine"], 10); + assert_eq!(first["edges"].as_array().ok_or("edges")?.len(), 2); + assert_eq!(first["edges"][0]["evidence"][0]["rule"], "fixture"); + assert_eq!(first["edges"][1]["relationshipSite"]["startLine"], 4); + let other = rows + .iter() + .find(|r| r["node"]["id"] == unusual_id) + .ok_or("other")?; + assert!(other["edges"][0].get("id").is_none()); + Ok(()) + } + + #[test] + fn mcp_neighbor_limit_is_an_error_not_empty_success() -> Result<(), Box> + { + let temp = tempfile::tempdir()?; + let path = temp.path().join("bounded.json"); + fs::write( + &path, + serde_json::to_vec( + &json!({"nodes":[{"id":"a","label":"Alpha","extra":"x".repeat(compass_query::MAX_NEIGHBOR_RESPONSE_BYTES)}],"links":[]}), + )?, + )?; + let result = CompassMcp::new(&path).invoke_result( + "get_neighbors", + &mut json!({"label":"a"}).as_object().ok_or("args")?.clone(), + ); + assert!( + matches!(result,Err(InvocationError::InvalidParams(message)) if message.contains("byte limit")) + ); + Ok(()) + } + #[test] fn mcp_neighbor_filter_precedes_neighbor_grouping() -> Result<(), Box> { let temp = tempfile::tempdir()?; diff --git a/crates/compass-mcp/tests/coverage_paths.rs b/crates/compass-mcp/tests/coverage_paths.rs index 753c2c511..7b1e4010d 100644 --- a/crates/compass-mcp/tests/coverage_paths.rs +++ b/crates/compass-mcp/tests/coverage_paths.rs @@ -318,6 +318,24 @@ async fn in_memory_protocol_exercises_tool_and_resource_server_handlers() "compass.hub-connectivity/1" ); assert!(structured["result"]["nodes"][0]["connectivity"]["edgeRecords"].is_u64()); + let neighbors = client + .call_tool( + CallToolRequestParams::new("get_neighbors").with_arguments(args(&[ + ("label", json!("a")), + ("relation_filter", json!("calls")), + ])), + ) + .await?; + let neighbors = neighbors + .structured_content + .ok_or("missing neighbor identities")?; + assert_eq!(neighbors["result"]["schema"], "compass.query.neighbors/1"); + assert_eq!(neighbors["result"]["neighbors"][0]["node"]["id"], "b"); + assert_eq!( + neighbors["result"]["neighbors"][0]["edges"][0]["source"], + "a" + ); + assert_eq!(neighbors["transportTruncation"]["truncated"], false); let path = client .call_tool( CallToolRequestParams::new("shortest_path") diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index 44501b9a2..ab7c8644d 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -13,6 +13,7 @@ mod export_binding; mod graph_engine; mod index; mod intent; +mod neighbors; mod program_join; mod ranking; mod recall; @@ -51,6 +52,10 @@ pub use intent::{ NaturalQueryIntent, NaturalQueryPlan, NaturalQueryRequest, QUERY_PLANNER_PROFILE_V1, plan_natural_query, }; +pub use neighbors::{ + MAX_NEIGHBOR_ADJACENCY_ENTRIES, MAX_NEIGHBOR_RECORDS, MAX_NEIGHBOR_RESPONSE_BYTES, + NeighborDirection, NeighborError, NeighborGroup, NeighborReport, direct_neighbors, +}; pub use program_join::join_program_evidence; pub use ranking::QUERY_RANKER_PROFILE_V1; pub use relevance::{ diff --git a/crates/compass-query/src/neighbors.rs b/crates/compass-query/src/neighbors.rs new file mode 100644 index 000000000..517cca1a0 --- /dev/null +++ b/crates/compass-query/src/neighbors.rs @@ -0,0 +1,335 @@ +//! Bounded, lossless incident-record projection for direct navigation. + +use std::collections::{BTreeMap, BTreeSet}; +use std::io::Write; + +use compass_model::{EdgeRecord, Graph, NodeIndex, NodeRecord}; +use serde::Serialize; +use thiserror::Error; + +pub const MAX_NEIGHBOR_ADJACENCY_ENTRIES: usize = 1_000_000; +pub const MAX_NEIGHBOR_RECORDS: usize = 10_000; +pub const MAX_NEIGHBOR_RESPONSE_BYTES: usize = 1_048_576; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum NeighborDirection { + Outgoing, + Incoming, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NeighborGroup<'a> { + pub direction: NeighborDirection, + pub node: &'a NodeRecord, + /// Complete records, including parallel occurrences and unknown attributes. + pub edges: Vec<&'a EdgeRecord>, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NeighborReport<'a> { + pub schema: &'static str, + /// Directions describe persisted endpoints, even on undirected artifacts. + pub direction_basis: &'static str, + pub graph_directed: bool, + pub seed: &'a NodeRecord, + pub relation_filter: String, + pub neighbors: Vec>, + pub truncated: bool, +} + +#[derive(Debug, Error)] +pub enum NeighborError { + #[error("neighbor seed is absent from the selected graph")] + MissingSeed, + #[error("neighbor relation filter exceeds 4096 bytes")] + FilterLimit, + #[error("neighbor lookup exceeds its adjacency-entry limit ({0})")] + AdjacencyLimit(usize), + #[error("neighbor lookup exceeds its matching-record limit ({0})")] + RecordLimit(usize), + #[error("neighbor result exceeds its byte limit ({0}); narrow relation_filter")] + ResponseLimit(usize), + #[error("cannot encode neighbor evidence: {0}")] + Encoding(#[from] serde_json::Error), +} + +struct ByteCounter { + bytes: usize, + limit: usize, + exceeded: bool, +} + +impl Write for ByteCounter { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + if bytes.len() > self.limit.saturating_sub(self.bytes) { + self.exceeded = true; + return Err(std::io::Error::other("neighbor byte limit")); + } + self.bytes += bytes.len(); + Ok(bytes.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +impl ByteCounter { + fn count(&mut self, value: &impl Serialize) -> Result<(), NeighborError> { + let result = serde_json::to_writer(&mut *self, value); + if self.exceeded { + return Err(NeighborError::ResponseLimit(self.limit)); + } + result?; + Ok(()) + } +} + +/// Project exact destinations and every matching incident record from one graph. +/// +/// No partial result is returned on exhaustion. Nodes sort by direction then ID; +/// records sort by canonical JSON, preserving identical parallel records. A self +/// loop appears in both directions, but counts once against the record budget. +/// Supply a full-record graph, not a compact traversal projection. +pub fn direct_neighbors<'a>( + graph: &'a Graph, + seed: NodeIndex, + relation_filter: &str, +) -> Result, NeighborError> { + bounded_neighbors( + graph, + seed, + relation_filter, + MAX_NEIGHBOR_ADJACENCY_ENTRIES, + MAX_NEIGHBOR_RECORDS, + MAX_NEIGHBOR_RESPONSE_BYTES, + ) +} + +fn bounded_neighbors<'a>( + graph: &'a Graph, + seed: NodeIndex, + relation_filter: &str, + max_adjacency: usize, + max_records: usize, + max_bytes: usize, +) -> Result, NeighborError> { + if seed >= graph.node_count() { + return Err(NeighborError::MissingSeed); + } + if relation_filter.len() > 4096 { + return Err(NeighborError::FilterLimit); + } + let filter = relation_filter.to_lowercase(); + let seed_node = graph.node(seed); + let mut bytes = ByteCounter { + bytes: 0, + limit: max_bytes, + exceeded: false, + }; + bytes.count(seed_node)?; + let mut seen = BTreeSet::new(); + let mut matched = 0; + let mut groups = BTreeMap::<(NeighborDirection, &str), Vec<(Vec, &EdgeRecord)>>::new(); + for (examined, index) in graph + .outgoing_edges(seed) + .chain(graph.incoming_edges(seed)) + .enumerate() + { + if examined >= max_adjacency { + return Err(NeighborError::AdjacencyLimit(max_adjacency)); + } + if !seen.insert(index) { + continue; + } + let edge = graph.edge(index); + // Bound encoding and normalization even for a nonmatching record. + let mut edge_bytes = ByteCounter { + bytes: 0, + limit: max_bytes, + exceeded: false, + }; + edge_bytes.count(edge)?; + if !filter.is_empty() && !edge.string("relation").to_lowercase().contains(&filter) { + continue; + } + if matched >= max_records { + return Err(NeighborError::RecordLimit(max_records)); + } + matched += 1; + let mut canonical = serde_json::to_value(edge)?; + canonical.sort_all_objects(); + let key = serde_json::to_vec(&canonical)?; + for (direction, applies, other) in [ + ( + NeighborDirection::Outgoing, + edge.source == seed_node.id, + edge.target.as_str(), + ), + ( + NeighborDirection::Incoming, + edge.target == seed_node.id, + edge.source.as_str(), + ), + ] { + if !applies { + continue; + } + let node = graph.node_index(other).ok_or(NeighborError::MissingSeed)?; + if !groups.contains_key(&(direction, other)) { + bytes.count(graph.node(node))?; + } + bytes.count(edge)?; + groups + .entry((direction, other)) + .or_default() + .push((key.clone(), edge)); + } + } + let neighbors = groups + .into_iter() + .map(|((direction, id), mut edges)| { + edges.sort_by(|left, right| left.0.cmp(&right.0)); + let index = graph.node_index(id).ok_or(NeighborError::MissingSeed)?; + Ok(NeighborGroup { + direction, + node: graph.node(index), + edges: edges.into_iter().map(|(_, edge)| edge).collect(), + }) + }) + .collect::, NeighborError>>()?; + let report = NeighborReport { + schema: "compass.query.neighbors/1", + direction_basis: "stored-endpoints", + graph_directed: graph.is_directed(), + seed: seed_node, + relation_filter: filter, + neighbors, + truncated: false, + }; + // Include envelope keys and repeated direction/node fields in the final cap. + ByteCounter { + bytes: 0, + limit: max_bytes, + exceeded: false, + } + .count(&report)?; + Ok(report) +} + +#[cfg(test)] +mod tests { + use super::*; + use compass_model::GraphDocument; + use serde_json::{Value, json}; + + fn graph(directed: bool, reverse: bool) -> Result> { + let mut nodes = vec![ + json!({"id":"seed","label":"run"}), + json!({"id":"b","label":"close","source":{"file":"b.rs","startLine":5}}), + json!({"id":"c","label":"close","source_file":"c.rs","source_location":"L9"}), + ]; + let mut links = vec![ + json!({"id":"edge-b","source":"seed","target":"b","relation":"calls","relationshipSite":{"file":"a.rs","startLine":1},"evidence":[{"origin":"ast","custom":{"x":1}}]}), + json!({"id":"edge-b2","source":"seed","target":"b","relation":"calls","relationshipSite":{"file":"a.rs","startLine":2}}), + json!({"source":"seed","target":"b","relation":"contains"}), + json!({"id":"edge-c","source":"c","target":"seed","relation":"calls"}), + json!({"id":"loop","source":"seed","target":"seed","relation":"calls"}), + ]; + links.push(links[0].clone()); + if reverse { + nodes.reverse(); + links.reverse(); + } + let document: GraphDocument = serde_json::from_value( + json!({"directed":directed,"multigraph":true,"nodes":nodes,"links":links}), + )?; + Ok(Graph::from_traversal_document(document)?) + } + + #[test] + fn neighbors_preserve_direction_parallel_records_self_loops_and_evidence() + -> Result<(), Box> { + for directed in [false, true] { + let graph = graph(directed, false)?; + let report = + direct_neighbors(&graph, graph.node_index("seed").ok_or("seed")?, "CALLS")?; + assert_eq!(report.neighbors.len(), 4); + assert_eq!(report.graph_directed, directed); + assert_eq!(report.direction_basis, "stored-endpoints"); + let group = &report.neighbors[0]; + assert_eq!(group.node.id, "b"); + assert_eq!(group.direction, NeighborDirection::Outgoing); + assert_eq!(group.edges.len(), 3); + assert_eq!(group.node.attributes["source"]["startLine"], 5); + assert_eq!(group.edges[0].attributes["evidence"][0]["custom"]["x"], 1); + assert_eq!(group.edges[0], group.edges[1]); + assert_eq!(report.neighbors[1].node.id, "seed"); + assert_eq!(report.neighbors[2].node.id, "c"); + assert_eq!(report.neighbors[2].direction, NeighborDirection::Incoming); + assert_eq!(report.neighbors[3].node.id, "seed"); + } + Ok(()) + } + + #[test] + fn neighbors_are_stable_under_graph_order_and_preserve_same_label_ids() + -> Result<(), Box> { + let mut outputs = Vec::::new(); + for reverse in [false, true] { + let graph = graph(true, reverse)?; + outputs.push(serde_json::to_value(direct_neighbors( + &graph, + graph.node_index("seed").ok_or("seed")?, + "", + )?)?); + } + assert_eq!(outputs[0], outputs[1]); + assert_eq!( + outputs[0]["neighbors"][0]["edges"] + .as_array() + .ok_or("edges")? + .len(), + 4 + ); + Ok(()) + } + + #[test] + fn neighbors_fail_explicitly_on_every_bound_and_distinguish_empty_result() + -> Result<(), Box> { + let graph = graph(true, false)?; + let seed = graph.node_index("seed").ok_or("seed")?; + assert!(matches!( + bounded_neighbors(&graph, seed, "", 0, 100, 100000), + Err(NeighborError::AdjacencyLimit(0)) + )); + assert!(matches!( + bounded_neighbors(&graph, seed, "absent", 0, 100, 100000), + Err(NeighborError::AdjacencyLimit(0)) + )); + assert!(matches!( + bounded_neighbors(&graph, seed, "calls", 100, 1, 100000), + Err(NeighborError::RecordLimit(1)) + )); + assert!(matches!( + bounded_neighbors(&graph, seed, "calls", 100, 100, 1), + Err(NeighborError::ResponseLimit(1)) + )); + assert!(matches!( + direct_neighbors(&graph, seed, &"x".repeat(4097)), + Err(NeighborError::FilterLimit) + )); + assert!(matches!( + direct_neighbors(&graph, graph.node_count(), ""), + Err(NeighborError::MissingSeed) + )); + let empty = direct_neighbors(&graph, seed, "absent")?; + assert!(empty.neighbors.is_empty()); + assert!(!empty.truncated); + Ok(()) + } +} diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index d8aebab9b..a0c546211 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -919,10 +919,33 @@ legacy prefix/substring lookup remains available. Multiple exact candidates produce an ambiguity list instead of neighbors. Candidates are sorted by exact ID, with at most 20 displayed and an explicit omission count. Retry using an exact ID to choose a declaration. Multiple fuzzy -candidates are also ambiguous. The text result and input schema are unchanged. -A successful result reports distinct incoming/outgoing neighbors after applying -`relation_filter`; use typed call-query results for individual occurrences and -source sites. Displayed neighbor labels alone may still be ambiguous. +candidates are also ambiguous. Input arguments are unchanged. +A successful result keeps the compact incoming/outgoing neighbor text and adds +escaped identity/source lines. Machine consumers use the additive structured +result `compass.query.neighbors/1` in `compass.mcp.tool-result/1`: + +- `seed`: the full node record for the resolved identity; +- `neighbors`: groups ordered by direction (`outgoing`, then `incoming`) and + node ID; each group has `direction`, the full `node` record and all matching + `edges`, ordered by canonical JSON; +- `directionBasis: "stored-endpoints"` and `graphDirected`: distinguish stored + endpoint orientation from the artifact's directed/undirected interpretation; +- `relationFilter`: the case-folded substring filter; `truncated: false` means + the complete filtered adjacency fit all bounds. + +Node records retain their graph source fields; relationship records retain IDs, +occurrence anchors, provenance and unknown attributes. Legacy absent IDs or +anchors are not invented. Parallel records survive; a self loop is represented +in both directions. The text line uses the first canonical edge for its relation +and confidence; consult `edges` for every record. An exact destination ID can be +used as the next `get_neighbors.label` without resolving a display name. + +Hard bounds are 1,000,000 examined adjacency entries, 10,000 matching incident +records (self loops counted once), a 4,096-byte filter and a 1 MiB structured +result. Limit exhaustion returns an error with no partial adjacency. A successful +empty `neighbors` array means no matching records, not a limit failure. Graph +loading and the outer MCP transport retain their existing bounds. This projection +uses one full snapshot rather than the compact traversal cache. ### Agent Query View From 8eb1bf0fbede6ffa6353a64343870f992a546dd5 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 02:46:17 -0700 Subject: [PATCH 61/97] audit: compare explicit neighbor identities and source-assisted followups --- benchmarks/agent_query/COVERAGE_PLAN.md | 14 + benchmarks/agent_query/README.md | 14 + .../agent_query/community_navigation.py | 20 +- benchmarks/agent_query/neighbor_identity.py | 92 +++++++ .../neighbor_identity_development_review.json | 241 ++++++++++++++++++ .../tests/test_neighbor_identity.py | 63 +++++ ...ode-graph-intelligence-audit-2026-09-26.md | 65 +++++ 7 files changed, 506 insertions(+), 3 deletions(-) create mode 100644 benchmarks/agent_query/neighbor_identity.py create mode 100644 benchmarks/agent_query/neighbor_identity_development_review.json create mode 100644 benchmarks/agent_query/tests/test_neighbor_identity.py diff --git a/benchmarks/agent_query/COVERAGE_PLAN.md b/benchmarks/agent_query/COVERAGE_PLAN.md index 902d1f58b..a5bd581f1 100644 --- a/benchmarks/agent_query/COVERAGE_PLAN.md +++ b/benchmarks/agent_query/COVERAGE_PLAN.md @@ -272,3 +272,17 @@ Graphify graphs, actual payload costs and development-only scope explicit. Final unchanged-source validation passed, including the native baseline and production fixture qualification. This does not establish overall superiority or population precision. + + +### Explicit neighbor identities + +`neighbor_identity_development_registration.json` freezes the unchanged-graph +five-repository comparison; `neighbor_identity_development_review.json` records +the verified capture. Compass now returns the exact reviewed destination ID +in all 14 direct neighbor responses. Graphify does not emit neighbor IDs, but +with one additional source-anchored public resolver call **both resolve 14/14**. +Label presence remains 14/14 and global target-label uniqueness remains 11/14 +for each. All 15 Compass full-record projections match their graphs, preserving +181 record appearances. This is graph consistency, not source precision for all +records. The extra source coordinates, richer Compass payload cost and reused +development scope remain explicit; no overall superiority claim follows. diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 7eba2ceae..7ee7d92af 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -467,3 +467,17 @@ all community assignments remain unchanged. Final-source validation passed, including the native baseline and production fixture qualification. These reused development tasks do not establish broad precision, god-object diagnosis or overall superiority. + + +### Explicit neighbor identities + +`neighbor_identity_development_registration.json` freezes the unchanged-graph +five-repository comparison; `neighbor_identity_development_review.json` records +the verified capture. Compass now returns the exact reviewed destination ID +in all 14 direct neighbor responses. Graphify does not emit neighbor IDs, but +with one additional source-anchored public resolver call **both resolve 14/14**. +Label presence remains 14/14 and global target-label uniqueness remains 11/14 +for each. All 15 Compass full-record projections match their graphs, preserving +181 record appearances. This is graph consistency, not source precision for all +records. The extra source coordinates, richer Compass payload cost and reused +development scope remain explicit; no overall superiority claim follows. diff --git a/benchmarks/agent_query/community_navigation.py b/benchmarks/agent_query/community_navigation.py index 79857c1f8..09cd70314 100644 --- a/benchmarks/agent_query/community_navigation.py +++ b/benchmarks/agent_query/community_navigation.py @@ -102,7 +102,8 @@ def execute(args): registration_bytes = read_bounded(args.registration, MAX_SOURCE_BYTES) run_bytes = read_bounded(args.run, MAX_JSON_BYTES) policy, registration, run = map(json.loads, [policy_bytes, registration_bytes, run_bytes]) - identity_mode = policy.get('schema') == 'compass.community-identity-navigation-policy/1' + destination_mode = policy.get('schema') == 'compass.neighbor-identity-navigation-policy/1' + identity_mode = destination_mode or policy.get('schema') == 'compass.community-identity-navigation-policy/1' if policy.get('schema') != 'compass.community-navigation-policy/1' and not identity_mode: raise ValueError('unsupported workflow policy') if registration.get('schema') != 'compass.community-task-pairs/1': @@ -125,7 +126,7 @@ def execute(args): raise ValueError('invalid direct-call task list') verify_environment(args) args.output.mkdir(parents=True, exist_ok=False) - report = dict(schema='compass.community-identity-navigation-capture/1' if identity_mode else 'compass.community-navigation-capture/1', complete=False, + report = dict(schema='compass.neighbor-identity-navigation-capture/1' if destination_mode else 'compass.community-identity-navigation-capture/1' if identity_mode else 'compass.community-navigation-capture/1', complete=False, policySha256=digest(policy_bytes), registrationSha256=digest(registration_bytes), sourceRunSha256=digest(run_bytes), sourceRun=str(args.run.resolve()), graphifyEnvironmentSha256=_sha256_file(args.graphify_environment), @@ -134,7 +135,7 @@ def execute(args): report['servers'][tool] = dict(path=str(path), sha256=_sha256_file(path)) for path in [args.policy, args.registration, args.graphify_environment, *[ Path(__file__).with_name(name) for name in ['community_navigation.py', 'community_tasks.py', - 'mcp_transport.py', 'mcp_compare.py', 'mcp_audit.py', 'runner.py', 'community_identity.py']]]: + 'mcp_transport.py', 'mcp_compare.py', 'mcp_audit.py', 'runner.py', 'community_identity.py', 'neighbor_identity.py']]]: shutil.copy2(path, args.output/path.name) report['supportFiles'][path.name] = _sha256_file(path) def save(): @@ -224,6 +225,19 @@ def save(): checked['addressedCollaboratorLabelPresent'] = completed and checked['collaboratorDisplayed'] checked['addressedCollaboratorIdentitySupported'] = (checked['addressedCollaboratorLabelPresent'] and len(checked['collaboratorLabelCandidates']) == 1) + if destination_mode: + from benchmarks.agent_query.neighbor_identity import destination_request, followup_score, audit_direct + target_id = target['matchedNodeIds'][0] if len(target['matchedNodeIds']) == 1 else None + if row['directCallRequired']: + request = destination_request(tool, neighbors, task['declarations'][1]) + row['destinationRequest'] = request + if request is not None and failure is None: + destination = call(session, request['method'], request['arguments']) + row['destinationCall'] = destination + if 'captureError' in destination: + failure = destination['captureError'] + row['destinationAudit'] = followup_score(tool, destination, task['declarations'][1], target_id) + row['identityAudit'] = audit_direct(neighbors, graph, tool, seed['matchedNodeIds'][0], target_id) # Scoring is after requests, never selector preparation. row['communityAudit'] = audit_membership(dict(repository=name, tool=tool, question='community', **community), graph) diff --git a/benchmarks/agent_query/neighbor_identity.py b/benchmarks/agent_query/neighbor_identity.py new file mode 100644 index 000000000..33fd6fe76 --- /dev/null +++ b/benchmarks/agent_query/neighbor_identity.py @@ -0,0 +1,92 @@ +"""Output-only follow-ups and post-request identity/projection checks. + +Direct identity is different from globally unique labels and from a second, +source-assisted resolver call. Graph consistency does not score source precision. +""" +from collections import Counter, defaultdict +import json + +from benchmarks.agent_query.community_identity import SEARCH_LIMITS, selected_id +from benchmarks.agent_query.community_navigation import NEIGHBOR +from benchmarks.agent_query.runner import _terminal_symbol + + +def destination_request(tool, neighbor_call, target): + """No graph argument: only displayed outgoing calls and public coordinates.""" + if not neighbor_call.get('executionSucceeded'): + return None + labels = sorted({name for direction, name, relation in NEIGHBOR.findall(neighbor_call.get('text', '')) + if direction == '-->' and 'calls' in relation.lower() + and _terminal_symbol(name) == target['symbol']}) + if not labels: + return None + if tool == 'compass': + return dict(method='search_symbols', arguments=dict(query=target['symbol'], **SEARCH_LIMITS)) + if tool == 'graphify': + if '::' in target['file']: + return None + return dict(method='get_node', arguments=dict(label=target['file']+'::'+target['symbol'])) + raise ValueError('unsupported tool') + + +def followup_score(tool, call, target, expected_id): + choice = selected_id(tool, call, target) + return dict(selection=choice, sourceMatchedDestination=expected_id is not None and choice['selector'] == expected_id) + + +def canonical(value): + return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False) + + +def audit_direct(call, graph, tool, seed, target): + result = dict(status='unavailable', explicitDestinationSupported=False, + fullProjectionMatches=False, returnedGroups=0, returnedRecordAppearances=0) + if not call.get('executionSucceeded'): + result['status'] = 'tool-error' + return result + envelope = call.get('response', {}).get('result', {}).get('structuredContent') + # Graphify 0.9.67 emits labels/relations/sites, with no neighbor identity + # field. Absence is unavailable, never guessed from global label uniqueness. + if envelope is None: + return result + result['status'] = 'invalid' + if (not isinstance(envelope, dict) or envelope.get('schema') != 'compass.mcp.tool-result/1' + or envelope.get('transportTruncation', {}).get('truncated') is not False): + return result + body = envelope.get('result') + if (not isinstance(body, dict) or body.get('schema') != 'compass.query.neighbors/1' + or body.get('truncated') is not False or body.get('directionBasis') != 'stored-endpoints' + or body.get('relationFilter') != 'calls'): + return result + nodes = {n['id']: n for n in graph['nodes']} + if body.get('seed') != nodes.get(seed) or body.get('graphDirected') is not graph.get('directed', False): + return result + groups = body.get('neighbors') + if not isinstance(groups, list) or len(groups) > 20000: + return result + expected = defaultdict(Counter) + for edge in graph['links']: + if 'calls' not in edge.get('kind' if tool == 'compass' else 'relation', '').lower(): + continue + if edge['source'] == seed: + expected['outgoing', edge['target']][canonical(edge)] += 1 + if edge['target'] == seed: + expected['incoming', edge['source']][canonical(edge)] += 1 + observed = {} + for group in groups: + if not isinstance(group, dict) or group.get('direction') not in {'incoming', 'outgoing'}: + return result + node, edges = group.get('node'), group.get('edges') + if not isinstance(node, dict) or not isinstance(node.get('id'), str) or node != nodes.get(node['id']): + return result + key = group['direction'], node['id'] + if key in observed or not isinstance(edges, list) or not edges or len(edges) > 10000: + return result + if any(not isinstance(edge, dict) for edge in edges): + return result + observed[key] = Counter(map(canonical, edges)) + result.update(status='checked', fullProjectionMatches=observed == expected, + returnedGroups=len(groups), returnedRecordAppearances=sum(sum(v.values()) for v in observed.values())) + result['explicitDestinationSupported'] = (observed == expected and target is not None + and bool(observed.get(('outgoing', target)))) + return result diff --git a/benchmarks/agent_query/neighbor_identity_development_review.json b/benchmarks/agent_query/neighbor_identity_development_review.json new file mode 100644 index 000000000..d4a2d66f8 --- /dev/null +++ b/benchmarks/agent_query/neighbor_identity_development_review.json @@ -0,0 +1,241 @@ +{ + "schema": "compass.neighbor-identity-development-review/1", + "scope": "Same-agent development replay on five pinned repositories and 15 reused source-defined tasks. Both receive identical public source coordinates and one optional destination resolver call. Not held-out or overall superiority.", + "protocolCommit": "bde19bb3687b8bcf8fb09fa0e9ef44c2bf87245c", + "implementationCommit": "d51698dc1cfb6fc7cf069926cb39964359859447", + "artifactRoot": "neighbor-identity-02", + "binarySha256": "59addb98d357f8ed9b09618a340df6e9dc1352357887d465eb1514c45cbd862f", + "version": "0.3.30", + "graphs": "All ten graph artifacts reused byte-for-byte from rust-index-receiver-03. No extraction timing comparison.", + "summary": { + "compass": { + "explicitDestinationSupported": 14, + "sourceMatchedDestinationFollowup": 14, + "fullProjectionsMatching": 15, + "returnedRecordAppearances": 181, + "calls": 59, + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11, + "callTextBytes": 193385, + "callResponseWireBytes": 1228383, + "fullSessionBytesIncludingRequestsAndStderr": 1323516 + }, + "graphify": { + "explicitDestinationSupported": 0, + "sourceMatchedDestinationFollowup": 14, + "fullProjectionsMatching": 0, + "returnedRecordAppearances": 0, + "calls": 59, + "tasks": 15, + "resolverIdsCorrect": 15, + "completedNeighborLookups": 15, + "directCallTasks": 14, + "collaboratorLabelPresent": 14, + "unambiguousTargetLabel": 11, + "callTextBytes": 57404, + "callResponseWireBytes": 63756, + "fullSessionBytesIncludingRequestsAndStderr": 107645 + } + }, + "responseDelta": { + "compass": { + "unchangedPriorCallPayloads": 30, + "changedPriorCallPayloads": 15, + "threeCallTextBytes": 178260, + "threeCallResponseWireBytes": 1017434, + "destinationTextBytes": 15125, + "destinationResponseWireBytes": 210949 + }, + "graphify": { + "unchangedPriorCallPayloads": 45, + "changedPriorCallPayloads": 0, + "threeCallTextBytes": 55402, + "threeCallResponseWireBytes": 60415, + "destinationTextBytes": 2002, + "destinationResponseWireBytes": 3341 + }, + "notes": "Payload comparisons ignore JSON-RPC request IDs, which shift after each added destination call. All 45 Graphify and 30 unchanged Compass community/seed resolver payloads match the baseline. Graphs are unchanged. Byte counts include actual new request IDs." + }, + "validation": { + "status": "passed", + "sourceCommit": "d51698dc1cfb6fc7cf069926cb39964359859447", + "binaryMatchesFinalBuild": true, + "counts": { + "neighbor-query-tests": { + "passed": 3, + "failed": 0, + "ignored": 0 + }, + "mcp-tests": { + "passed": 60, + "failed": 0, + "ignored": 0 + }, + "workspace-tests": { + "passed": 1106, + "failed": 0, + "ignored": 2 + }, + "product-tests": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 4.28 + }, + { + "name": "neighbor-query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--lib", + "neighbors::tests", + "--locked" + ], + "exitCode": 0, + "seconds": 0.65 + }, + { + "name": "mcp-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--locked" + ], + "exitCode": 0, + "seconds": 18.3 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 6.44 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 67.2 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 42.36 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.08 + }, + { + "name": "qualification", + "argv": [ + "bash", + "scripts/qualify_code_graph_v1.sh", + "--fixtures-only" + ], + "exitCode": 0, + "seconds": 633.17 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "--locked", + "-p", + "compass-cli", + "--bin", + "compass" + ], + "exitCode": 0, + "seconds": 2.22 + } + ], + "harnessTestsPassed": 134 + }, + "artifacts": { + "policy.json": "f4afd6b400a304c3fa032b116b8dc5a7ac3c1c469224c3753de1629425310cf7", + "input-recheck.json": "dde3db5d4317daa0877442c13aeff17a1bfea26c0470d3a626c4e8bacd26e3dc", + "baseline-direct-identity.json": "1319291b5904422723dfb0628eb96a99b2217382e162e229cf7f22860abd5bd2", + "binary-manifest.json": "b0b4ad1c5f88e16a285f3bcd0965964286c3a719a209a08832ac2b58e463b3c8", + "response-delta.json": "16cb77b1ce9afce2b6ce86134c52bd09f250fc9478e30f142b595202a0a267bf", + "navigation/capture.json": "c1d07c47906a891c2f6451fba31f30d11fb09d51882d7282c968d8914957dafa", + "navigation/verified-summary.json": "14507fffe0381e5f586936b8e28a1459366d8f1ff9184c1432ba517d7a23f88a", + "navigation/verify_capture.py": "0cbc8ad24a1209d7d8fc559d702c2ca185c242ee1fa60dc3f7b3be776c98dc00", + "harness-tests.log": "9eb7ec8be37dc0d41169d0ffb240f71a0173fd9229709563beb5ce6398e2e0bb", + "validation.json": "a61555ab44db0fef8456ac1fffde4179d330e4038206b86b13399aa18370156f", + "final-validation-summary.json": "796b22e12ca5adba0eecf30919df31043c507a8b6c1bbdbc69d7f8e821e7024d", + "validate.py": "4ba05b9d6bd17515fb5460737c2b6300d6d80ade92e6ab3e1cbebdddb224b542", + "fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "neighbor-query-tests-final.log": "665afacd150175b0dfc4fb0fd94f99df9f4be09e36219277b3e3786caca3a33c", + "mcp-tests-final.log": "94154f9cbe14c380ae4ea21bbb9ff2fe5744b7a06483dade40eb8528e1ef6158", + "clippy-final.log": "c7ba7a8dd8e81a05bac04776058a3efcb131a1e59e19a80a621ebbdfcfe55873", + "workspace-tests-final.log": "cfa81eb3b3924767ad551ecd3203c53451c3a5b2ae76b879bf689acc40d1a5e9", + "product-tests-final.log": "0155aace32defe5fcc6b306984b3fdbba651c0b54ba83c0d9281ab2bdd0c7a0e", + "product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "qualification-final.log": "141b8e13f6062cd3447a2ca12d5976ef6edc494dd87b678b92781ef7fc8977ba", + "build-final.log": "2b0c38ed9b2d8b3b42ebca729444e4c5f8b4763c23bca63e77381d42bee0f513" + }, + "interpretation": "Compass now exposes exact destination identities directly for all 14 reviewed calls. Graphify exposes labels and sites in neighbor output, so explicit ID projection is unavailable. With one additional source-anchored public resolver call, both resolve 14/14 destinations. Label presence remains 14/14 and global label uniqueness remains 11/14 for each. This improves direct response identity and record fidelity, not end-to-end superiority in the extended workflow.", + "limitations": [ + "All 15 Compass full-record projections match their frozen graphs (181 record appearances). Graph consistency does not establish source precision for those other records.", + "Graphify full-record projection is unavailable, not a failed source-precision or graph-correctness score; its displayed adjacency remains consistent with its graphs.", + "59 public calls per tool include 14 symmetric destination follow-ups. A caller using Compass inline IDs would not require those follow-ups, but this capture executes them to compare resolver capabilities fairly.", + "Exact source coordinates are supplied; this is not unassisted discovery or autonomous long-walk evaluation.", + "Compass returns more evidence and uses more bytes. Native output richness and controls differ; byte counts are not equal-content efficiency.", + "God-object judgments, source-grounded responsibility explanations, broader precision, longer directed walks and held-out confirmation remain open.", + "The extra resolver uses supplied target coordinates; it confirms the requested declaration, not an autonomous reconstruction of an ambiguous edge from a label. All 14 reviewed pairs on both sides also have the source-required directed call in their frozen graph." + ], + "graphifyEnvironmentSha256": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535" +} diff --git a/benchmarks/agent_query/tests/test_neighbor_identity.py b/benchmarks/agent_query/tests/test_neighbor_identity.py new file mode 100644 index 000000000..bfd8f1746 --- /dev/null +++ b/benchmarks/agent_query/tests/test_neighbor_identity.py @@ -0,0 +1,63 @@ +import copy +import unittest + +from benchmarks.agent_query.neighbor_identity import audit_direct, destination_request + + +class NeighborIdentityTests(unittest.TestCase): + def fixture(self): + graph=dict(directed=True,nodes=[dict(id='a',name='run'),dict(id='b',name='close'),dict(id='c',name='close')], + links=[dict(id='call',source='a',target='b',kind='calls',evidence=[dict(origin='ast')])]) + body=dict(schema='compass.query.neighbors/1', directionBasis='stored-endpoints',graphDirected=True, + seed=graph['nodes'][0], relationFilter='calls', truncated=False, + neighbors=[dict(direction='outgoing',node=graph['nodes'][1],edges=graph['links'])]) + call=dict(executionSucceeded=True,response=dict(result=dict(structuredContent=dict( + schema='compass.mcp.tool-result/1',transportTruncation=dict(truncated=False),result=copy.deepcopy(body))))) + return graph,call + + def test_explicit_identity_is_supported_even_when_label_collides(self): + graph,call=self.fixture() + result=audit_direct(call,graph,'compass','a','b') + self.assertTrue(result['explicitDestinationSupported']) + self.assertTrue(result['fullProjectionMatches']) + + def test_mutated_ids_records_anchors_direction_and_truncation_cannot_pass(self): + for variant in ['id','edge','direction','seed','node','duplicate','missing','truncated','schema','transport']: + graph,call=self.fixture(); envelope=call['response']['result']['structuredContent']; b=envelope['result'] + if variant=='id': b['neighbors'][0]['node']['id']='c' + elif variant=='edge': b['neighbors'][0]['edges'][0]['evidence'][0]['origin']='invented' + elif variant=='direction': b['neighbors'][0]['direction']='incoming' + elif variant=='seed': b['seed']['id']='c' + elif variant=='node': b['neighbors'][0]['node']['source']=dict(file='invented.rs',startLine=1) + elif variant=='duplicate': b['neighbors'].append(copy.deepcopy(b['neighbors'][0])) + elif variant=='missing': b['neighbors'][0]['edges']=[] + elif variant=='truncated': b['truncated']=True + elif variant=='schema': b['schema']='compass.query.neighbors/99' + else: envelope['transportTruncation']['truncated']=True + self.assertFalse(audit_direct(call,graph,'compass','a','b')['explicitDestinationSupported'],variant) + + def test_legacy_text_does_not_turn_unique_labels_into_explicit_ids(self): + graph,call=self.fixture() + call['response']['result'].pop('structuredContent') + call['text']='Neighbors of run:\n --> close [calls] [EXTRACTED]' + for tool in ['compass','graphify']: + self.assertFalse(audit_direct(call,graph,tool,'a','b')['explicitDestinationSupported']) + + def test_parallel_record_loss_and_extra_records_fail_projection(self): + graph,call=self.fixture() + graph['links'].append(copy.deepcopy(graph['links'][0])) + self.assertFalse(audit_direct(call,graph,'compass','a','b')['fullProjectionMatches']) + call['response']['result']['structuredContent']['result']['neighbors'][0]['edges'].append(copy.deepcopy(graph['links'][0])) + self.assertTrue(audit_direct(call,graph,'compass','a','b')['fullProjectionMatches']) + + def test_followup_uses_only_returned_outgoing_symbol_and_public_coordinates(self): + target=dict(file='src/a.py',symbol='close',startLine=10) + call=dict(executionSucceeded=True,text='Neighbors of run:\n --> close() [calls] [EXTRACTED]') + self.assertEqual(destination_request('compass',call,target)['arguments']['query'],'close') + self.assertEqual(destination_request('graphify',call,target)['arguments']['label'],'src/a.py::close') + for text in [' <-- close() [calls] [EXTRACTED]',' --> close() [contains] [EXTRACTED]', ' --> other() [calls] [EXTRACTED]']: + self.assertIsNone(destination_request('compass',dict(executionSucceeded=True,text=text),target)) + self.assertIsNone(destination_request('compass',dict(executionSucceeded=False,text=call['text']),target)) + + +if __name__=='__main__': unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 17d6088ea..960b1d70b 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2272,6 +2272,71 @@ the frozen evaluated binary is byte-identical to the final build. Commands, counts and log hashes are recorded in the review artifact. Broader real-repository qualification and fresh held-out evaluation were not run in this development arm. +## Explicit neighbor identities and relationship evidence + +Protocol `bde19bb3` freezes this development arm before implementation +`d51698dc`. It reuses all ten graph artifacts and the same five pinned source +repositories from the Rust recovery arm. All 15 tasks retain their original +community, seed resolver and neighbor calls. A new symmetric arm permits one +additional destination resolver call on each of the 14 direct-call tasks, +gated on an actually returned outgoing collaborator label. Both tools receive +the same target file, declaration start and terminal symbol for that follow-up. +This is source-assisted navigation on reused tasks, not held-out discovery. + +Compass neighbor results now carry `compass.query.neighbors/1`: exact seed and +destination node records and all matching relationship records. The query layer +preserves parallel records, self loops, endpoint direction, source anchors and +provenance under explicit adjacency/record/byte bounds. Text retains compact +neighbor lines and adds escaped identity/source details. The graph artifacts, +extraction, source trees, package version and historical realizations do not +change in this arm. + +A separate same-agent verifier recomputes every request, response, source-anchor +selection and full incident-record multiset. All 15 Compass projections match +their graphs, totaling 181 record appearances across the requests. That includes +records beyond the 14 reviewed source collaborators and is **graph consistency, +not broader source precision**. All 45 prior Graphify payloads and all 30 Compass +community/seed resolver payloads match the baseline, ignoring request IDs that +shift after the extra calls. All 15 Compass neighbor payloads change. + +| Development measure | Compass | Graphify | +| --- | ---: | ---: | +| Correct seed ID and completed neighbor lookup | 15/15 | 15/15 | +| Reviewed outgoing collaborator label | 14/14 | 14/14 | +| Globally unambiguous target label | 11/14 | 11/14 | +| Explicit target ID in direct neighbor response | 14/14 | Unavailable (0/14) | +| Correct target ID after one extra source-anchored resolver | 14/14 | 14/14 | + +Before this change neither tool emitted explicit neighbor IDs. Graphify's +neighbor response still emits labels, relations and source sites; the lack of a +full record projection is not a wrong-edge judgment. Its additional public +`get_node` call resolves all 14 source-matched targets, including the three +ambiguous labels. Compass's richer direct response avoids that extra identity +lookup for these tasks, but the extended workflow remains a tie. The extra +resolver uses supplied target coordinates; it does not autonomously reconstruct +an ambiguous edge from its label. All 14 reviewed pairs on both sides also have +the required directed call in their frozen graph. + +All 118 public calls succeed. With all four steps executed symmetrically, +Compass uses 193,385 text bytes, 1,228,383 response-wire bytes and 1,323,516 +full-session bytes. Graphify uses 57,404, 63,756 and 107,645 respectively. For +just the original three steps, response-wire totals are 1,017,434 versus 60,415 +bytes. These are observed costs with different native payloads and controls; +there is no equal-content efficiency or latency claim. + +Artifact hashes, commands and limitations are recorded in +`benchmarks/agent_query/neighbor_identity_development_review.json`; raw captures +and logs are under `neighbor-identity-02`. Final validation passed: formatting, +3 focused query tests, all 60 MCP tests, workspace Clippy, 1,106 workspace tests +(2 ignored), 9 product tests, the product-boundary check, complete production +fixture qualification and the final CLI build. The Python harness passed all +134 tests. Runtime/test hashes stayed unchanged during validation and match +`d51698dc`; the evaluated binary is byte-identical to the final build. The gate +retains its existing fixture-omission and compiler warnings in the logs. +God-object responsibility judgments, richer source explanations, broader +assertion precision, longer directed walks and fresh held-out confirmation +remain outstanding. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 98759ae818a89f89fad19d4219b9c8954b986108 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 02:50:01 -0700 Subject: [PATCH 62/97] audit: freeze directed identity and depth-bound comparison --- ...ted_identity_development_registration.json | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 benchmarks/agent_query/directed_identity_development_registration.json diff --git a/benchmarks/agent_query/directed_identity_development_registration.json b/benchmarks/agent_query/directed_identity_development_registration.json new file mode 100644 index 000000000..a5678095f --- /dev/null +++ b/benchmarks/agent_query/directed_identity_development_registration.json @@ -0,0 +1,25 @@ +{ + "schema": "compass.directed-identity-development-registration/1", + "scope": "Same five source-reviewed directed call chains from the existing development panel. New public endpoint-resolution workflow and depth-bound diagnostics, not held-out evidence or longer-path coverage.", + "baselineCommit": "8eb1bf0fbede6ffa6353a64343870f992a546dd5", + "baselineBinary": "neighbor-identity-02/compass", + "baselineBinarySha256": "59addb98d357f8ed9b09618a340df6e9dc1352357887d465eb1514c45cbd862f", + "sourceWitnesses": { + "directed_path_development_registration.json": "749eaca9649b1975f099445b4eab7a17259b4578a01747aaa7182a622d0282e6", + "directed_path_development_review.json": "82ef7fd83173a7935e1729c44651324bdd76ae46c58198d5c5ebab17abf192bd" + }, + "graphRun": "rust-index-receiver-03/run.json", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "graphs": "Reuse all ten frozen graphs without extraction. Verify hashes and pinned sources before and after.", + "endpointResolution": "Supply the same witness endpoint file, start line and symbol to both tools. Make one public search_symbols request for Compass (max_candidates 256, max_nodes 500, max_response_bytes 524288) and one path::symbol get_node request for Graphify per endpoint. Use existing source-anchor selectors. Request builders cannot read graph IDs/adjacency. Unresolved endpoints remain in the denominator; no oracle-ID substitution.", + "pathRequests": "For each pair of uniquely source-matched returned IDs, invoke Compass node SOURCE TARGET --max-depth 8 --max-paths 1 --format json and Graphify path SOURCE TARGET --directed. Repeat swapped endpoints as a diagnostic; a forward witness does not prove reverse absence. Preserve all errors and output.", + "bounds": "60 seconds and 16 MiB per stdout/stderr stream for each CLI call; 60 seconds and 1 MiB per MCP response. Native internal limits differ (Graphify has no CLI depth flag); no equal-work, latency or extraction comparison.", + "pathJudgment": "Verify ordered exact identities, directed relationship kinds, adjacency, confidence, anchors and source support for every returned route. Registered routes are positive witnesses, not mandatory shortest routes. Review alternate routes against source before credit. Mixed containment/reference routes are not call-chain successes.", + "depthDiagnostics": "For Compass, repeat source-resolved forward IDs with each max_depth from 1 through one below the source witness hop count. Review any found route. Missing bounded results must not assert global direction mismatch or unreachability without proof. Graphify has no equivalent depth control, so this arm is a Compass correctness diagnostic, not a paired score.", + "fixValidation": "First reproduce a longer forward path plus shorter reverse shortcut in a native regression. Preserve successful paths at the boundary, exhausted work budgets, dead-end and cycle cases, deterministic ordering, and JSON/store parity. Run applicable native baseline and public contracts.", + "limitations": [ + "Reused tasks, graphs and known misses; no held-out or population claims.", + "Resolution, path availability, route identity, source-supported call flow and bound correctness are separate outcomes.", + "God-object responsibility, source explanations and broader/longer walks remain outstanding." + ] +} From 2cc02501dbf5f77bc99bcb2afc13ed4a4529a4c3 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:01:00 -0700 Subject: [PATCH 63/97] audit: register native label control for directed identity comparison --- .../directed_identity_label_control_registration.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 benchmarks/agent_query/directed_identity_label_control_registration.json diff --git a/benchmarks/agent_query/directed_identity_label_control_registration.json b/benchmarks/agent_query/directed_identity_label_control_registration.json new file mode 100644 index 000000000..6e1288887 --- /dev/null +++ b/benchmarks/agent_query/directed_identity_label_control_registration.json @@ -0,0 +1,11 @@ +{ + "schema": "compass.directed-identity-label-control-registration/1", + "parentProtocolCommit": "98759ae8", + "reason": "The first ID-input capture showed Graphify CLI path selecting a wrong starting node despite using a publicly resolved ID. Preserve that result as cross-interface identity evidence, and retain a separate native label-input comparison rather than presenting it as pure path-algorithm quality.", + "timing": "Registered after observing the ID baseline and the earlier short-label development panel, before executing this label control on the current fixed graph pair. Not blind or held-out.", + "inputs": "Use exactly the original five witness source and target short names from directed_path_development_registration.json. No IDs, new aliases, post-output candidates, retries or scope changes.", + "commands": "Compass node SOURCE TARGET --max-depth 8 --max-paths 1 --format json --graph GRAPH; Graphify path SOURCE TARGET --directed --graph GRAPH.", + "artifacts": "Use the same ten frozen graphs, final evaluated Compass binary and hash-verified Graphify environment as the source-assisted arm.", + "bounds": "60 seconds and 16 MiB per stdout/stderr stream. Internal work limits differ; no speed ranking.", + "scoring": "Keep all five positive tasks. Review full ordered node identity, directed call edges, source anchors and alternate routes with the same source witnesses. Ambiguity, wrong endpoints, mixed-relation paths, limits and missing paths are distinct failures to complete a reviewed call-chain request. Never combine the best outputs from ID and label arms into a new primary score." +} From 1ec835bfa66073b9295ac5c58ca089e3959c5c7c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:13:33 -0700 Subject: [PATCH 64/97] fix(query): preserve incomplete depth-bound trail results --- CHANGELOG.md | 5 + COMPATIBILITY.md | 15 ++ crates/compass-cli/tests/code_query_cli.rs | 94 +++++++++++ crates/compass-query/src/code_query.rs | 70 ++++++++- .../tests/bounded_path_oracle.rs | 38 ++++- crates/compass-query/tests/code_traversal.rs | 147 ++++++++++++++++++ docs/reference/outputs.md | 13 ++ 7 files changed, 374 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a325e661d..5d12cad5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Report incomplete directed trail searches when the depth frontier remains + unexplored, instead of inferring a direction mismatch from a shorter + undirected route. Preserve successful bounded paths and closed negatives; + retain incomplete status in exploration even when no path is returned. + - Add exact destination identities and full relationship records to MCP neighbor results, preserving source anchors, provenance, direction and parallel calls. Bound adjacency work and response size; report exhaustion explicitly. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index c5d2abf09..17cc943a1 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -200,6 +200,21 @@ Groups sort by outgoing/incoming direction then exact ID; records sort by canonical JSON. Self loops appear in both directions and count once against the incident-record budget. Published graphs and historical artifacts are unchanged. +### Directed trail depth diagnostics + +Typed `node`/`get_node` trails now report `truncated: true` with +`bounded_truncation` when an unsuccessful search reaches its depth bound and +cannot prove the reachable frontier is closed. They do not use a shorter +undirected route to assert a global direction mismatch while an unexplored +forward continuation remains. Frontier checks share the declared work budget +and run only after the bounded path search fails, preserving positive paths +within the requested depth. Closed dead ends and cycles can still yield complete +negative results. The `compass.query/1` schema is unchanged; consumers should +continue to distinguish incomplete searches from negative answers. + +`explore` / `explore_code` also retain incomplete-search status when no connecting +path was found; previously that status could be lost with the absent path. + ### Bounded node trails The undirected `path` command also retains nondominated cost/depth states. diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index ad1dec707..2fdf78c5b 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -2160,3 +2160,97 @@ fn natural_query_and_explain_accept_agent_controlled_budgets_and_pages() assert!(out_of_range.stderr.contains("last available page")); Ok(()) } + +#[test] +fn node_command_reports_depth_exhaustion_without_claiming_wrong_direction() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = support::write_typed_graph(directory.path())?; + let mut graph = GraphDocument::load(&path)?; + let template = graph.nodes.first().cloned().ok_or("node template")?; + graph.nodes = ["n:s", "n:a", "n:b", "n:t"] + .into_iter() + .map(|id| { + let mut node = template.clone(); + node.id = id.into(); + node.name = id.into(); + node.qualified_name = id.into(); + node.kind = NodeKind::Function; + node + }) + .collect(); + let template = graph + .links + .iter() + .find(|edge| edge.kind == EdgeKind::Calls) + .cloned() + .ok_or("edge template")?; + graph.links = [ + ("n:s", "n:a"), + ("n:a", "n:b"), + ("n:b", "n:t"), + ("n:t", "n:s"), + ] + .into_iter() + .map(|(source, target)| { + let mut edge = template.clone(); + edge.source = source.into(); + edge.target = target.into(); + edge.occurrence_rule = None; + edge.id = compass_model::identity::edge_id( + source, + EdgeKind::Calls, + target, + edge.relationship_site.as_ref(), + None, + ); + edge.key.clone_from(&edge.id); + edge + }) + .collect(); + std::fs::write(&path, serde_json::to_vec(&graph)?)?; + for depth in ["2", "3"] { + let output = std::process::Command::new(env!("CARGO_BIN_EXE_compass")) + .args([ + "node", + "n:s", + "n:t", + "--max-depth", + depth, + "--format", + "json", + "--graph", + ]) + .arg(&path) + .output()?; + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let result: Value = serde_json::from_slice(&output.stdout)?; + assert_eq!(result["schema"], "compass.query/1"); + if depth == "2" { + assert_eq!(result["truncated"], true); + assert_eq!(result["paths"], serde_json::json!([])); + let diagnostics = result["diagnostics"].as_array().ok_or("diagnostics")?; + assert!( + diagnostics + .iter() + .any(|d| d["code"] == "bounded_truncation") + ); + assert!( + !diagnostics + .iter() + .any(|d| d["code"] == "direction_mismatch" || d["code"] == "no_match") + ); + } else { + assert_eq!(result["truncated"], false); + assert_eq!( + result["paths"][0]["nodeIds"], + serde_json::json!(["n:s", "n:a", "n:b", "n:t"]) + ); + } + } + Ok(()) +} diff --git a/crates/compass-query/src/code_query.rs b/crates/compass-query/src/code_query.rs index bcec1c653..c0e648e00 100644 --- a/crates/compass-query/src/code_query.rs +++ b/crates/compass-query/src/code_query.rs @@ -3019,20 +3019,21 @@ impl CodeQueryEngine { response.truncated = true; break; } - if let [source, target] = pair - && let (Some((nodes, edges)), truncated) = self.shortest_path( + if let [source, target] = pair { + let (path, truncated) = self.shortest_path( source, target, request.include_heuristic, &request.limits, &mut budget, false, - )? - { + )?; response.truncated |= truncated; - ids.extend(nodes.iter().cloned()); - edge_ids.extend(edges.iter().cloned()); - response.paths.push(self.path_record(&nodes, &edges)?); + if let Some((nodes, edges)) = path { + ids.extend(nodes.iter().cloned()); + edge_ids.extend(edges.iter().cloned()); + response.paths.push(self.path_record(&nodes, &edges)?); + } } } self.add_nodes(&ids, &mut response)?; @@ -3506,6 +3507,7 @@ impl CodeQueryEngine { let mut admitted = HashSet::from([source.to_owned()]); let mut predecessor = HashMap::::new(); let mut truncated = false; + let mut depth_frontier = BTreeSet::new(); while let Some(Reverse((cost, depth, path_key, node))) = queue.pop() { self.check_deadline()?; let state = (node.clone(), depth); @@ -3536,6 +3538,7 @@ impl CodeQueryEngine { return Ok((Some((nodes, edges)), truncated)); } if depth >= max_depth { + depth_frontier.insert(node); continue; } let mut adjacent = Vec::new(); @@ -3603,6 +3606,59 @@ impl CodeQueryEngine { queue.push(Reverse((next_cost, next_depth, next_key, next))); } } + // Probe the depth frontier only after the bounded search fails. Doing + // this while the queue still has viable states would spend their shared + // work budget and could hide a valid, costlier, shorter route. + if !truncated { + for node in depth_frontier { + self.check_deadline()?; + // A shallower arrival was fully expanded already. Its outgoing + // records therefore cannot hide an unexplored continuation. + if best + .get(&node) + .is_some_and(|labels| labels.keys().any(|depth| *depth < max_depth)) + { + continue; + } + let (incident, incomplete) = if directed { + self.backend.matching_bounded( + &node, + false, + ALL_EDGE_KINDS, + include_heuristic, + budget.remaining_edges, + )? + } else { + self.backend.incident_bounded( + &node, + include_heuristic, + budget.remaining_edges, + )? + }; + if incomplete { + truncated = true; + break; + } + for edge in incident { + if !budget.consume_edge() { + truncated = true; + break; + } + let next = if edge.source == node { + &edge.target + } else { + &edge.source + }; + if !admitted.contains(next) { + truncated = true; + break; + } + } + if truncated { + break; + } + } + } Ok((None, truncated)) } diff --git a/crates/compass-query/tests/bounded_path_oracle.rs b/crates/compass-query/tests/bounded_path_oracle.rs index 0008582dc..5c8200ce7 100644 --- a/crates/compass-query/tests/bounded_path_oracle.rs +++ b/crates/compass-query/tests/bounded_path_oracle.rs @@ -55,6 +55,29 @@ fn hop_oracle(matrix: &Matrix, max_depth: usize) -> Option<(usize, u32)> { visit(matrix, 0, max_depth, 1) } +// Independent unweighted closure of nodes reachable within the hop bound. +// A missing bounded path is incomplete if the frontier can reach unseen nodes. +fn open_frontier(matrix: &Matrix, max_depth: usize) -> bool { + let mut reachable = [true, false, false, false]; + for _ in 0..max_depth { + let previous = reachable; + for (source, row) in matrix.iter().enumerate() { + if previous[source] { + for (target, edge) in row.iter().enumerate() { + reachable[target] |= edge.is_some(); + } + } + } + } + matrix.iter().enumerate().any(|(source, row)| { + reachable[source] + && row + .iter() + .enumerate() + .any(|(target, edge)| edge.is_some() && !reachable[target]) + }) +} + #[test] fn all_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box> { let directory = tempfile::tempdir()?; @@ -223,8 +246,8 @@ fn all_path_engines_match_exhaustive_four_node_oracles() -> Result<(), Box Result<(), Box Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = directory.path().join("graph.json"); + write_weighted_trail_fixture( + &graph_path, + &[ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:a", EdgeKind::Calls, "n:b"), + ("n:b", EdgeKind::Calls, "n:t"), + ("n:t", EdgeKind::Calls, "n:s"), + ], + )?; + for reverse in [false, true] { + let mut graph = GraphDocument::load(&graph_path)?; + if reverse { + graph.nodes.reverse(); + graph.links.reverse(); + fs::write(&graph_path, serde_json::to_vec(&graph)?)?; + } + let store = SqliteStore::open(directory.path().join(format!("store-{reverse}.db")))?; + let prepared = GraphSnapshotBuilder::new().prepare(&store, &graph)?; + GraphSnapshotBuilder::new().activate(&store, &prepared)?; + for engine in [ + open(&graph_path, None, &directory.path().join("cache"))?, + open_with_store( + &store, + &graph_path, + None, + &directory.path().join("store-cache"), + )?, + ] { + for depth in [1, 2, 3] { + let result = engine.node_trail(NodeTrailRequest { + source: "n:s".into(), + target: "n:t".into(), + include_heuristic: false, + limits: CodeQueryLimits { + max_depth: depth, + ..CodeQueryLimits::default() + }, + })?; + if depth < 3 { + assert!(result.paths.is_empty()); + assert!(result.truncated, "{result:?}"); + assert!( + result + .diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::BoundedTruncation) + ); + assert!(!result.diagnostics.iter().any(|d| matches!( + d.code, + QueryDiagnosticCode::DirectionMismatch | QueryDiagnosticCode::NoMatch + ))); + } else { + assert!(!result.truncated); + assert_eq!(result.paths.len(), 1); + assert_eq!(result.paths[0].node_ids, ["n:s", "n:a", "n:b", "n:t"]); + } + } + } + } + Ok(()) +} + +#[test] +fn node_trail_depth_frontier_proves_closed_dead_ends_and_cycles() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + for cycle in [false, true] { + let path = directory.path().join(format!("closed-{cycle}.json")); + let mut edges = vec![ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:a", EdgeKind::Calls, "n:b"), + ]; + if cycle { + edges.push(("n:b", EdgeKind::Calls, "n:a")); + } + write_weighted_trail_fixture(&path, &edges)?; + let graph = GraphDocument::load(&path)?; + let store = SqliteStore::open(directory.path().join(format!("closed-{cycle}.db")))?; + let prepared = GraphSnapshotBuilder::new().prepare(&store, &graph)?; + GraphSnapshotBuilder::new().activate(&store, &prepared)?; + for engine in [ + open(&path, None, &directory.path().join("cache"))?, + open_with_store(&store, &path, None, &directory.path().join("store-cache"))?, + ] { + let response = engine.node_trail(NodeTrailRequest { + source: "n:s".into(), + target: "n:t".into(), + include_heuristic: false, + limits: CodeQueryLimits { + max_depth: 2, + ..CodeQueryLimits::default() + }, + })?; + assert!(!response.truncated, "{response:?}"); + assert!(response.paths.is_empty()); + assert!( + response + .diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::NoMatch) + ); + } + } + Ok(()) +} + +#[test] +fn explore_keeps_depth_exhaustion_when_no_connecting_path_is_returned() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + write_weighted_trail_fixture( + &path, + &[ + ("n:s", EdgeKind::Calls, "n:a"), + ("n:a", EdgeKind::Calls, "n:b"), + ("n:b", EdgeKind::Calls, "n:t"), + ], + )?; + let graph = GraphDocument::load(&path)?; + let store = SqliteStore::open(directory.path().join("store.db"))?; + let prepared = GraphSnapshotBuilder::new().prepare(&store, &graph)?; + GraphSnapshotBuilder::new().activate(&store, &prepared)?; + for engine in [ + open(&path, None, &directory.path().join("cache"))?, + open_with_store(&store, &path, None, &directory.path().join("store-cache"))?, + ] { + let response = engine.explore(compass_model::query_contract::ExploreRequest { + symbols: vec!["n:s".into(), "n:t".into()], + root: directory.path().to_string_lossy().into_owned(), + include_heuristic: false, + limits: CodeQueryLimits { + max_depth: 2, + ..CodeQueryLimits::default() + }, + })?; + assert!(response.paths.is_empty()); + assert!(response.truncated, "{response:?}"); + } + Ok(()) +} diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index a0c546211..5d66667b5 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -1348,3 +1348,16 @@ allocating an unbounded JSON graph. **Next step:** identify the most structured available output for your consumer and validate its major version/direction/multiplicity before reading values. + +### Directed trail depth limits + +For typed `node` and MCP `get_node`, an unsuccessful search that leaves an open +depth frontier returns `truncated: true` and `bounded_truncation`. An undirected +shortcut is not proof that no longer directed route exists. Frontier checks use +the same edge budget and run after the search for a bounded positive path. +Closed dead ends and cycles can return complete negative results; increasing +`max_depth` may resolve an incomplete result. The machine schema remains +`compass.query/1`. + +`explore` / `explore_code` also retain incomplete-search status when no connecting +path was found; previously that status could be lost with the absent path. From 47ad5eba99ea2be915d2c786dd1a56597bfb2eb1 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:15:21 -0700 Subject: [PATCH 65/97] audit: compare directed identity workflows and depth diagnostics --- benchmarks/agent_query/directed_identity.py | 119 + .../directed_identity_development_review.json | 2081 +++++++++++++++++ .../tests/test_directed_identity.py | 23 + ...ode-graph-intelligence-audit-2026-09-26.md | 91 + 4 files changed, 2314 insertions(+) create mode 100644 benchmarks/agent_query/directed_identity.py create mode 100644 benchmarks/agent_query/directed_identity_development_review.json create mode 100644 benchmarks/agent_query/tests/test_directed_identity.py diff --git a/benchmarks/agent_query/directed_identity.py b/benchmarks/agent_query/directed_identity.py new file mode 100644 index 000000000..8acdf1eda --- /dev/null +++ b/benchmarks/agent_query/directed_identity.py @@ -0,0 +1,119 @@ +"""Capture source-assisted directed paths and separate Compass depth diagnostics. + +Request construction uses public resolver outputs and frozen source coordinates; +no graph ID or route is consulted to choose endpoints. All failures are retained. +""" +import argparse +import dataclasses +import hashlib +import json +from pathlib import Path +import shutil +import subprocess + +from benchmarks.agent_query.community_identity import SEARCH_LIMITS, selected_id +from benchmarks.agent_query.community_navigation import call +from benchmarks.agent_query.community_tasks import read_bounded +from benchmarks.agent_query.mcp_compare import verify_environment +from benchmarks.agent_query.mcp_transport import StdioMcp +from benchmarks.agent_query.runner import run_bounded, _sha256_file + + +def resolver(tool, witness): + coordinate = dict(file=witness['file'], startLine=witness['line'], symbol=witness['symbol']) + if tool == 'compass': + return coordinate, dict(method='search_symbols', arguments=dict(query=coordinate['symbol'], **SEARCH_LIMITS)) + if tool == 'graphify' and '::' not in coordinate['file']: + return coordinate, dict(method='get_node', arguments=dict(label=coordinate['file']+'::'+coordinate['symbol'])) + raise ValueError('unsupported resolver input') + + +def execute(args): + registration = json.loads(read_bounded(args.registration, 1048576)) + witnesses = json.loads(read_bounded(args.witnesses, 1048576)) + run = json.loads(read_bounded(args.run, 16777216)) + if registration['schema'] != 'compass.directed-identity-development-registration/1': + raise ValueError('unsupported registration') + if _sha256_file(args.run) != registration['graphRunSha256']: + raise ValueError('graph input digest mismatch') + if _sha256_file(args.witnesses) != registration['sourceWitnesses'][args.witnesses.name]: + raise ValueError('source witness digest mismatch') + verify_environment(args) + repositories = {r['repository']: r for r in run['repositories']} + if len(witnesses['witnesses']) != 5: + raise ValueError('expected the frozen five source chains') + def inputs(): + for w in witnesses['witnesses']: + repo = repositories[w['repository']]; root = Path(repo['source']) + if subprocess.check_output(['git', '-C', str(root), 'rev-parse', 'HEAD'], text=True, timeout=10).strip() != w['commit']: + raise ValueError('source revision mismatch') + if subprocess.check_output(['git', '-C', str(root), 'status', '--porcelain'], text=True, timeout=10).strip(): + raise ValueError('source checkout is dirty') + for tool in ['compass', 'graphify']: + if _sha256_file(Path(repo[tool+'Graph'])) != repo[tool+'GraphSha256']: + raise ValueError('graph digest mismatch') + for anchor in w['nodes'] + [s['site'] for s in w['steps']]: + path = (root/anchor['file']).resolve() + if not path.is_relative_to(root.resolve()): raise ValueError('source path escapes root') + data = read_bounded(path, 4194304) + if hashlib.sha256(data).hexdigest() != anchor['fileSha256']: + raise ValueError('source file digest mismatch') + if anchor['text'] not in data.decode().splitlines()[anchor['line']-1]: + raise ValueError('source witness text mismatch') + inputs() + args.output.mkdir(parents=True, exist_ok=False) + files = [args.registration, args.witnesses, args.graphify_environment, Path(__file__)] + files += [Path(__file__).with_name(n) for n in ['community_identity.py','community_navigation.py','mcp_compare.py','mcp_transport.py','runner.py']] + support = {} + for p in files: + shutil.copy2(p,args.output/p.name); support[p.name]=_sha256_file(p) + binaries={name:dict(path=str(path),sha256=_sha256_file(path)) for name,path in [('compass',args.compass),('graphify-python',args.graphify_python)]} + report=dict(schema='compass.directed-identity-capture/1',complete=False,sourceRun=str(args.run),sourceRunSha256=_sha256_file(args.run),supportFiles=support,binaries=binaries,results=[]) + def save(): (args.output/'capture.json').write_text(json.dumps(report,indent=2)+'\n') + save() + for witness in witnesses['witnesses']: + repo=repositories[witness['repository']]; root=Path(repo['source']) + for tool in ['compass','graphify']: + row=dict(question=witness['id'],repository=witness['repository'],tool=tool,graphSha256=repo[tool+'GraphSha256'],resolvers=[],paths=[]) + argv=[str(args.compass),'serve'] if tool=='compass' else [str(args.graphify_python),'-m','graphify.serve'] + argv+=['--graph',repo[tool+'Graph'],'--transport','stdio'] + directory=args.output/'raw'/witness['id']/tool + try: + with StdioMcp(argv,root,directory/'mcp',timeout=60,max_bytes=1048576) as session: + session.initialize() + listing=session.send('tools/list',{}) + expected='search_symbols' if tool=='compass' else 'get_node' + if expected not in {t['name'] for t in listing.get('result',{}).get('tools',[])}: + raise ValueError('public resolver unavailable') + for endpoint in [witness['nodes'][0],witness['nodes'][-1]]: + coordinate,request=resolver(tool,endpoint) + captured=call(session,request['method'],request['arguments']) + row['resolvers'].append(dict(coordinate=coordinate,call=captured,selection=selected_id(tool,captured,coordinate))) + except (OSError,RuntimeError,ValueError,TimeoutError) as error: + row['resolverError']=str(error) + selectors=[r['selection']['selector'] for r in row['resolvers']] + row['endpointsResolved']=len(selectors)==2 and all(s is not None for s in selectors) + if row['endpointsResolved']: + plans=[('forward',selectors[0],selectors[1],8),('reverse',selectors[1],selectors[0],8)] + if tool=='compass':plans += [('depth-'+str(depth),selectors[0],selectors[1],depth) for depth in range(1,len(witness['steps']))] + for direction,source,target,depth in plans: + argv=([str(args.compass),'node',source,target,'--max-depth',str(depth),'--max-paths','1','--format','json'] if tool=='compass' else [str(args.graphify_python),'-m','graphify','path',source,target,'--directed']) + argv+=['--graph',repo[tool+'Graph']] + stdout=directory/(direction+'.stdout');stderr=directory/(direction+'.stderr') + capture=run_bounded(tuple(argv),cwd=root,timeout_seconds=60,stdout_path=stdout,stderr_path=stderr) + record=dataclasses.asdict(capture);record.pop('stdout');record.pop('stderr') + record.update(direction=direction,maxDepth=depth if tool=='compass' else None,stdoutPath=str(stdout),stderrPath=str(stderr),stdoutSha256=_sha256_file(stdout),stderrSha256=_sha256_file(stderr)) + row['paths'].append(record) + report['results'].append(row);save() + print(witness['id'],tool,row['endpointsResolved'],[(p['direction'],p['exit_code']) for p in row['paths']],flush=True) + inputs();verify_environment(args) + for binary in binaries.values(): + if _sha256_file(Path(binary['path']))!=binary['sha256']:raise ValueError('executable changed') + report['complete']=True;save() + + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__) + for name in ['registration','witnesses','run','output','compass','graphify-python','graphify-environment']: + p.add_argument('--'+name,type=Path,required=True) + execute(p.parse_args()) diff --git a/benchmarks/agent_query/directed_identity_development_review.json b/benchmarks/agent_query/directed_identity_development_review.json new file mode 100644 index 000000000..1d398e240 --- /dev/null +++ b/benchmarks/agent_query/directed_identity_development_review.json @@ -0,0 +1,2081 @@ +{ + "schema": "compass.directed-identity-development-review/1", + "scope": "Known development chains across five languages; query replay on ten frozen graphs. Source-assisted IDs and native short labels are separate arms. No held-out, extraction, latency, population precision, or overall superiority claim.", + "implementationCommit": "1ec835bfa66073b9295ac5c58ca089e3959c5c7c", + "registrations": { + "sourceAssisted": { + "commit": "98759ae818a89f89fad19d4219b9c8954b986108", + "sha256": "13f2d40857c320b89958c606d0b673f6232824c4ea88cd6fd107bbbb5b8a56b5" + }, + "nativeLabelControl": { + "commit": "2cc02501", + "sha256": "2201598e675a94675c4f133cc900b7e083018abb0268289a6d59853d1a6b0eb4", + "timing": "After ID baseline failures were seen, before executing label control. Not blind." + } + }, + "tools": { + "compass": { + "version": "0.3.30", + "binarySha256": "58199f5cfbbce9def3acb58bf019179218c8f6cbe379a3cd96ad9542e8f0b31f" + }, + "graphify": { + "version": "0.9.67", + "environmentManifestSha256": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535" + } + }, + "graphs": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassGraphSha256": "e68fbe798dcf7422184736971dcdfc29d577e1e37269ea9c43456b9f6af54cb3", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "summary": { + "sourceAssisted": { + "compass": { + "sourceSupportedCallPaths": 4, + "literalFrozenOccurrenceSites": 3, + "positiveTasks": 5 + }, + "graphify": { + "sourceSupportedCallPaths": 1, + "literalFrozenOccurrenceSites": 1, + "positiveTasks": 5 + } + }, + "nativeLabels": { + "compass": { + "sourceSupportedCallPaths": 2, + "literalFrozenOccurrenceSites": 1, + "positiveTasks": 5 + }, + "graphify": { + "sourceSupportedCallPaths": 2, + "literalFrozenOccurrenceSites": 2, + "positiveTasks": 5 + } + }, + "unchangedPayloads": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "direction": "forward" + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "direction": "forward" + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "direction": "reverse" + }, + { + "question": "click-directed-chain", + "tool": "compass", + "direction": "forward" + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "direction": "forward" + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "direction": "reverse" + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "direction": "forward" + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "direction": "forward" + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "direction": "reverse" + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "direction": "forward" + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "direction": "reverse" + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "direction": "forward" + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "direction": "forward" + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "direction": "reverse" + } + ], + "depthDiagnostics": { + "beforeCompleteNegatives": 9, + "afterExplicitIncomplete": 9 + } + }, + "sourceAssistedResults": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + { + "id": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "source": { + "file": "mux.go", + "startByte": 4390, + "endByte": 4510, + "startLine": 137, + "startColumn": 0, + "endLine": 139, + "endColumn": 1 + }, + "name": ".MethodFunc()" + }, + { + "id": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "source": { + "file": "mux.go", + "startByte": 4034, + "endByte": 4267, + "startLine": 127, + "startColumn": 0, + "endLine": 133, + "endColumn": 1 + }, + "name": ".Method()" + }, + { + "id": "sha256:f347f98bee807aa7b86ad9bb7962bce9fffcacb0e94be392f6bca77e9d6c978e", + "source": { + "file": "mux.go", + "startByte": 13994, + "endByte": 14669, + "startLine": 430, + "startColumn": 0, + "endLine": 451, + "endColumn": 1 + }, + "name": ".handle()" + }, + { + "id": "sha256:eb099943707f1745c7673f0dbc8e5a32de0d98b853e4b355ab9633337b638aa6", + "source": { + "file": "tree.go", + "startByte": 3152, + "endByte": 5528, + "startLine": 148, + "startColumn": 0, + "endLine": 238, + "endColumn": 1 + }, + "name": ".InsertRoute()" + }, + { + "id": "sha256:14440f0795f62b6428da626ef794bf18b4acd793012fc5148478f8a50519ddaa", + "source": { + "file": "tree.go", + "startByte": 19345, + "endByte": 19481, + "startLine": 822, + "startColumn": 0, + "endLine": 829, + "endColumn": 1 + }, + "name": "longestPrefix()" + } + ], + "steps": [ + { + "edgeId": "sha256:275a2a085c7409a899e065c2d836a8a75506ddd76542ac01de4764e50e899b32", + "source": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "target": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "kind": "calls", + "site": { + "file": "mux.go", + "startByte": 4471, + "endByte": 4480, + "startLine": 138, + "startColumn": 1, + "endLine": 138, + "endColumn": 10 + }, + "sourceBytes": "mx.Method", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:3155b7566154b5aa0c12e18cd3b3049c0b96cc58b9d8a2f0fa38df38f24cb968", + "source": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "target": "sha256:f347f98bee807aa7b86ad9bb7962bce9fffcacb0e94be392f6bca77e9d6c978e", + "kind": "calls", + "site": { + "file": "mux.go", + "startByte": 4235, + "endByte": 4244, + "startLine": 132, + "startColumn": 1, + "endLine": 132, + "endColumn": 10 + }, + "sourceBytes": "mx.handle", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:a8a9ae5d8e8d38059bf3959a56f6ab77056b2a23faac85a18f88ce28c5fdebb5", + "source": "sha256:f347f98bee807aa7b86ad9bb7962bce9fffcacb0e94be392f6bca77e9d6c978e", + "target": "sha256:eb099943707f1745c7673f0dbc8e5a32de0d98b853e4b355ab9633337b638aa6", + "kind": "calls", + "site": { + "file": "mux.go", + "startByte": 14628, + "endByte": 14647, + "startLine": 450, + "startColumn": 8, + "endLine": 450, + "endColumn": 27 + }, + "sourceBytes": "mx.tree.InsertRoute", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:96d37619a29dcb60d5057938e6910b83c6068d29b5e9b01a983588d56070d55c", + "source": "sha256:eb099943707f1745c7673f0dbc8e5a32de0d98b853e4b355ab9633337b638aa6", + "target": "sha256:14440f0795f62b6428da626ef794bf18b4acd793012fc5148478f8a50519ddaa", + "kind": "calls", + "site": { + "file": "tree.go", + "startByte": 4577, + "endByte": 4590, + "startLine": 201, + "startColumn": 18, + "endLine": 201, + "endColumn": 31 + }, + "sourceBytes": "longestPrefix", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + } + ], + "responseDiagnostics": [], + "truncated": false, + "classification": "reviewed-call-chain", + "resolvedEndpoints": [ + "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "sha256:14440f0795f62b6428da626ef794bf18b4acd793012fc5148478f8a50519ddaa" + ] + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "route length differs from reviewed witness" + ], + "nodes": [], + "steps": [], + "renderedLabels": [ + "Mux", + "node", + ".findPattern()", + "longestPrefix()" + ], + "renderedRelations": [ + "references", + "method", + "calls" + ], + "classification": "mixed-relation-route", + "renderedEndpointCandidates": [ + [ + "mux_go_chi_mux" + ], + [ + "tree_longestprefix" + ] + ], + "resolvedEndpoints": [ + "chi_mux_methodfunc", + "tree_longestprefix" + ], + "renderedEndpointIdentityMatches": [ + false, + true + ] + }, + { + "question": "click-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": false, + "failures": [], + "nodes": [ + { + "id": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "source": { + "file": "src/click/utils.py", + "startByte": 11486, + "endByte": 13132, + "startLine": 381, + "startColumn": 0, + "endLine": 427, + "endColumn": 12 + }, + "name": "open_file()" + }, + { + "id": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "source": { + "file": "src/click/_compat.py", + "startByte": 11423, + "endByte": 14244, + "startLine": 374, + "startColumn": 0, + "endLine": 452, + "endColumn": 40 + }, + "name": "open_stream()" + }, + { + "id": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "source": { + "file": "src/click/_compat.py", + "startByte": 9593, + "endByte": 9800, + "startLine": 319, + "startColumn": 0, + "endLine": 323, + "endColumn": 17 + }, + "name": "get_binary_stdin()" + }, + { + "id": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "source": { + "file": "src/click/_compat.py", + "startByte": 4815, + "endByte": 5484, + "startLine": 176, + "startColumn": 0, + "endLine": 191, + "endColumn": 15 + }, + "name": "_find_binary_reader()" + }, + { + "id": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "source": { + "file": "src/click/_compat.py", + "startByte": 4230, + "endByte": 4529, + "startLine": 154, + "startColumn": 0, + "endLine": 160, + "endColumn": 55 + }, + "name": "_is_binary_reader()" + } + ], + "steps": [ + { + "edgeId": "sha256:b4f41add3eb26a6b6699864bed02e49dd24595a669501b1f2fbd44140601556e", + "source": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "target": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "kind": "calls", + "site": { + "file": "src/click/utils.py", + "startByte": 12980, + "endByte": 12991, + "startLine": 422, + "startColumn": 22, + "endLine": 422, + "endColumn": 33 + }, + "sourceBytes": "open_stream", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:b3d1f4a591b7a358eb841388e97117bbd4a2377817aa4014c2b9f5fed9759bc5", + "source": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "target": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 12085, + "endByte": 12101, + "startLine": 392, + "startColumn": 19, + "endLine": 392, + "endColumn": 35 + }, + "sourceBytes": "get_binary_stdin", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:ab477fcd547ac3d833934602fc1d5871a98378031c63dc125aeaebbf5e84cb34", + "source": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "target": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 9644, + "endByte": 9663, + "startLine": 320, + "startColumn": 13, + "endLine": 320, + "endColumn": 32 + }, + "sourceBytes": "_find_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:0ed10b05deb50320b392d13c0e611c3732abfc5d77e1267e3bb744afc3596349", + "source": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "target": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 5399, + "endByte": 5416, + "startLine": 188, + "startColumn": 27, + "endLine": 188, + "endColumn": 44 + }, + "sourceBytes": "_is_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": false, + "acceptedAlternateOccurrences": [ + { + "file": "src/click/_compat.py", + "line": 188, + "text": "if buf is not None and _is_binary_reader(buf, True):", + "reason": "Same caller and callee, second direct occurrence; independently source-adjudicated in the earlier panel and reread for this replay. Frozen line 181 remains distinct." + } + ] + } + ], + "responseDiagnostics": [], + "truncated": false, + "classification": "reviewed-call-chain", + "resolvedEndpoints": [ + "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8" + ] + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "expected exactly one rendered path header" + ], + "nodes": [], + "steps": [], + "classification": "no-path-reported", + "resolvedEndpoints": [ + "src_click_utils_open_file", + "src_click_compat_is_binary_reader" + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + { + "id": "sha256:577f3fb7ba180a8a08c3f3f8fe6bae0f7eedf731b281139a8e9d5f9b7213a7e6", + "source": { + "file": "src/main/java/org/jsoup/Jsoup.java", + "startByte": 19606, + "endByte": 19743, + "startLine": 434, + "startColumn": 4, + "endLine": 436, + "endColumn": 5 + }, + "name": ".isValid()" + }, + { + "id": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 5577, + "endByte": 6207, + "startLine": 124, + "startColumn": 4, + "endLine": 133, + "endColumn": 5 + }, + "name": ".isValidBodyHtml()" + }, + { + "id": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 8357, + "endByte": 8584, + "startLine": 182, + "startColumn": 4, + "endLine": 186, + "endColumn": 5 + }, + "name": ".copySafeNodes()" + } + ], + "steps": [ + { + "edgeId": "sha256:38d6c354f2e4a136528e0f8d4261fc07f6a2c9e18886865384966819efd9e046", + "source": "sha256:577f3fb7ba180a8a08c3f3f8fe6bae0f7eedf731b281139a8e9d5f9b7213a7e6", + "target": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "kind": "calls", + "site": { + "file": "src/main/java/org/jsoup/Jsoup.java", + "startByte": 19711, + "endByte": 19726, + "startLine": 435, + "startColumn": 37, + "endLine": 435, + "endColumn": 52 + }, + "sourceBytes": "isValidBodyHtml", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:d64cc76f4a39ece82f005ff9eb5b59046c4f4a2affc9183dbd7042047afe3c19", + "source": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "target": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "kind": "calls", + "site": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6102, + "endByte": 6115, + "startLine": 131, + "startColumn": 27, + "endLine": 131, + "endColumn": 40 + }, + "sourceBytes": "copySafeNodes", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + } + ], + "responseDiagnostics": [ + { + "code": "incomplete_coverage", + "message": "Published graph coverage is incomplete: partial graph published after quarantining 0 nodes and 2 edges with 0 identity collisions; 0 examples omitted by the diagnostic cap", + "nodeId": null, + "path": null + } + ], + "truncated": false, + "classification": "reviewed-call-chain", + "resolvedEndpoints": [ + "sha256:577f3fb7ba180a8a08c3f3f8fe6bae0f7eedf731b281139a8e9d5f9b7213a7e6", + "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3" + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "expected exactly one rendered path header" + ], + "nodes": [], + "steps": [], + "classification": "no-path-reported", + "resolvedEndpoints": [ + "src_main_java_org_jsoup_jsoup_jsoup_isvalid", + "src_main_java_org_jsoup_safety_cleaner_cleaner_copysafenodes" + ] + }, + { + "question": "redux-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "classification": "unresolved-endpoint", + "endpointSelections": [ + { + "status": "ambiguous", + "matchedIds": [ + "sha256:2dc819f91bc866786968c643e45ad6b3419369536fbc2f8851bb810298489506", + "sha256:df1114c0498b4e62ad32946ea04a508c76bd92955defafa89ab7857dbf577770" + ], + "selector": null, + "returnedAnchors": [ + { + "id": "sha256:1c38121eddeb189509bb8d8efb07f7d9f34bfa79e7fab77f18234722d8114e0a", + "file": "src/utils/kindOf.ts", + "line": 2, + "symbols": [ + "miniKindOf" + ] + }, + { + "id": "sha256:1c93d4d4f845f8b68d97822707d5e2d754f37e7ae6c69e307aab7ce195ef297a", + "file": "src/utils/kindOf.ts", + "line": 2, + "symbols": [ + "miniKindOf" + ] + }, + { + "id": "sha256:23d5e35138207bb498c86df080c5a0d26d80cfe44184d297441fb49ed763c9e6", + "file": "src/utils/kindOf.ts", + "line": 1, + "symbols": [ + "ts" + ] + }, + { + "id": "sha256:2dc819f91bc866786968c643e45ad6b3419369536fbc2f8851bb810298489506", + "file": "src/utils/kindOf.ts", + "line": 62, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:3b684eb33ecd37d5eea86502a36408c1047e2ff3c4ad5e0aed7ac9b9cb62710c", + "file": "src/createStore.ts", + "line": 13, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:61ac1d5c152c2db8ec8626b9e4c1b9f3e646935e7fc92425410935d3cf83c37e", + "file": "src/utils/kindOf.ts", + "line": 40, + "symbols": [ + "ctorName" + ] + }, + { + "id": "sha256:6e1928d086236a4cb8403dded8bf17d37f1abe5823b7091b4f7f6f7326bd9ec0", + "file": "src/utils/kindOf.ts", + "line": 53, + "symbols": [ + "isDate" + ] + }, + { + "id": "sha256:7c75317cab6859b47837e5821768e0e90c92a8cd8396164b9cc6e498d9d997cc", + "file": "src/combineReducers.ts", + "line": 12, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:8c815dd62745a090206d761cb274206829de83dc2f078a138c36c7b230ef0ee4", + "file": "src/utils/kindOf.ts", + "line": 63, + "symbols": [ + "typeOfVal" + ] + }, + { + "id": "sha256:b52e744f4e50b7f16a06ce02cc6cfc1f89b707e35e4ced168687d8603aaf0223", + "file": "src/bindActionCreators.ts", + "line": 7, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:bba968e55231038ae24c8fa0a1e745acd9241259443ab866eb7f63166f9360cf", + "file": "src/utils/kindOf.ts", + "line": 1, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:c6fb95e1a31cabfbecb5ee306b717a388f114ce7b1351712532bc2e75028095b", + "file": "src/utils/kindOf.ts", + "line": 6, + "symbols": [ + "type" + ] + }, + { + "id": "sha256:ddd22666a1a0e212c7d64e402d033e3d0051b79061d09d855ab5a0cbd600c9f9", + "file": "src/utils/kindOf.ts", + "line": 21, + "symbols": [ + "constructorName" + ] + }, + { + "id": "sha256:df1114c0498b4e62ad32946ea04a508c76bd92955defafa89ab7857dbf577770", + "file": "src/utils/kindOf.ts", + "line": 62, + "symbols": [ + "kindOf" + ] + }, + { + "id": "sha256:fa284cf3eb048c8e0b8d62d93c37c43aa43488a186096cef095e897c66a85253", + "file": "src/utils/kindOf.ts", + "line": 44, + "symbols": [ + "isError" + ] + } + ] + }, + { + "status": "resolved", + "matchedIds": [ + "sha256:61ac1d5c152c2db8ec8626b9e4c1b9f3e646935e7fc92425410935d3cf83c37e" + ], + "selector": "sha256:61ac1d5c152c2db8ec8626b9e4c1b9f3e646935e7fc92425410935d3cf83c37e", + "returnedAnchors": [ + { + "id": "sha256:61ac1d5c152c2db8ec8626b9e4c1b9f3e646935e7fc92425410935d3cf83c37e", + "file": "src/utils/kindOf.ts", + "line": 40, + "symbols": [ + "ctorName" + ] + } + ] + } + ] + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + "src_utils_kindof_kindof", + "src_utils_kindof_minikindof", + "src_utils_kindof_ctorname" + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 66, + "text": "typeOfVal = miniKindOf(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + } + ], + "renderedLabels": [ + "kindOf()", + "miniKindOf()", + "ctorName()" + ], + "renderedRelations": [ + "calls", + "calls" + ], + "classification": "reviewed-call-chain", + "renderedEndpointCandidates": [ + [ + "src_utils_kindof_kindof" + ], + [ + "src_utils_kindof_ctorname" + ] + ], + "resolvedEndpoints": [ + "src_utils_kindof_kindof", + "src_utils_kindof_ctorname" + ], + "renderedEndpointIdentityMatches": [ + true, + true + ] + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "source": { + "file": "src/lib.rs", + "startByte": 29143, + "endByte": 30869, + "startLine": 840, + "startColumn": 4, + "endLine": 882, + "endColumn": 5 + }, + "name": ".handle_entry()" + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "source": { + "file": "src/lib.rs", + "startByte": 34443, + "endByte": 34874, + "startLine": 961, + "startColumn": 4, + "endLine": 971, + "endColumn": 5 + }, + "name": ".follow()" + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "source": { + "file": "src/lib.rs", + "startByte": 34880, + "endByte": 35480, + "startLine": 973, + "startColumn": 4, + "endLine": 989, + "endColumn": 5 + }, + "name": ".check_loop()" + }, + { + "id": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "source": { + "file": "src/error.rs", + "startByte": 6769, + "endByte": 7076, + "startLine": 184, + "startColumn": 4, + "endLine": 196, + "endColumn": 5 + }, + "name": ".from_loop()" + } + ], + "steps": [ + { + "edgeId": "sha256:db51772e6e2a0f689b69f1447740b96e340679a37df7c50bfc727ffe85a06cf4", + "source": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "target": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 29337, + "endByte": 29348, + "startLine": 845, + "startColumn": 25, + "endLine": 845, + "endColumn": 36 + }, + "sourceBytes": "self.follow", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:9d2cbc7e60565250bba8045728a1451893838b7f1b8d26084a542957c88557c3", + "source": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "target": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 34811, + "endByte": 34826, + "startLine": 968, + "startColumn": 12, + "endLine": 968, + "endColumn": 27 + }, + "sourceBytes": "self.check_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:32f0ce77ab676891029891f1d836f95f3e4c8458dcee2b5d51f7128b8ad32bcf", + "source": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "target": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 35294, + "endByte": 35310, + "startLine": 981, + "startColumn": 27, + "endLine": 981, + "endColumn": 43 + }, + "sourceBytes": "Error::from_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + } + ], + "responseDiagnostics": [], + "truncated": false, + "classification": "reviewed-call-chain", + "resolvedEndpoints": [ + "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01" + ] + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "expected exactly one rendered path header" + ], + "nodes": [], + "steps": [], + "classification": "no-path-reported", + "resolvedEndpoints": [ + "src_lib_intoiter_handle_entry", + "src_error_error_from_loop" + ] + } + ], + "nativeLabelResults": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"MethodFunc\" matched 2 nodes", + "nodeId": null, + "path": null + } + ], + "truncated": false, + "classification": "ambiguous" + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "route length differs from reviewed witness" + ], + "nodes": [], + "steps": [], + "renderedLabels": [ + ".MethodFunc()", + ".Method()", + ".handle()", + "node", + ".findPattern()", + "longestPrefix()" + ], + "renderedRelations": [ + "calls", + "calls", + "references", + "method", + "calls" + ], + "classification": "mixed-relation-route", + "renderedEndpointCandidates": [ + [ + "chi_mux_methodfunc", + "chi_router_methodfunc" + ], + [ + "tree_longestprefix" + ] + ] + }, + { + "question": "click-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": false, + "failures": [], + "nodes": [ + { + "id": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "source": { + "file": "src/click/utils.py", + "startByte": 11486, + "endByte": 13132, + "startLine": 381, + "startColumn": 0, + "endLine": 427, + "endColumn": 12 + }, + "name": "open_file()" + }, + { + "id": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "source": { + "file": "src/click/_compat.py", + "startByte": 11423, + "endByte": 14244, + "startLine": 374, + "startColumn": 0, + "endLine": 452, + "endColumn": 40 + }, + "name": "open_stream()" + }, + { + "id": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "source": { + "file": "src/click/_compat.py", + "startByte": 9593, + "endByte": 9800, + "startLine": 319, + "startColumn": 0, + "endLine": 323, + "endColumn": 17 + }, + "name": "get_binary_stdin()" + }, + { + "id": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "source": { + "file": "src/click/_compat.py", + "startByte": 4815, + "endByte": 5484, + "startLine": 176, + "startColumn": 0, + "endLine": 191, + "endColumn": 15 + }, + "name": "_find_binary_reader()" + }, + { + "id": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "source": { + "file": "src/click/_compat.py", + "startByte": 4230, + "endByte": 4529, + "startLine": 154, + "startColumn": 0, + "endLine": 160, + "endColumn": 55 + }, + "name": "_is_binary_reader()" + } + ], + "steps": [ + { + "edgeId": "sha256:b4f41add3eb26a6b6699864bed02e49dd24595a669501b1f2fbd44140601556e", + "source": "sha256:67617fe84c7e933001c8e7a60628821e3924d23d180c92cd780bba23fd5f7d1d", + "target": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "kind": "calls", + "site": { + "file": "src/click/utils.py", + "startByte": 12980, + "endByte": 12991, + "startLine": 422, + "startColumn": 22, + "endLine": 422, + "endColumn": 33 + }, + "sourceBytes": "open_stream", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:b3d1f4a591b7a358eb841388e97117bbd4a2377817aa4014c2b9f5fed9759bc5", + "source": "sha256:a06c11e8745b06b0d60bc3f43308f19ae0c888cf7369a2a07bdf50adef36cda6", + "target": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 12085, + "endByte": 12101, + "startLine": 392, + "startColumn": 19, + "endLine": 392, + "endColumn": 35 + }, + "sourceBytes": "get_binary_stdin", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:ab477fcd547ac3d833934602fc1d5871a98378031c63dc125aeaebbf5e84cb34", + "source": "sha256:a6c6eac365b65fafaf2f46514d79c2cb36bb310fcfcb5119fcf65cca36ea35f9", + "target": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 9644, + "endByte": 9663, + "startLine": 320, + "startColumn": 13, + "endLine": 320, + "endColumn": 32 + }, + "sourceBytes": "_find_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:0ed10b05deb50320b392d13c0e611c3732abfc5d77e1267e3bb744afc3596349", + "source": "sha256:786c10821cf94664962fba44589b5e61c43e426efb6b21a72e0f75b8c62e16df", + "target": "sha256:e2c76ab69c159b40a93080ab5c1fa322863c8644d0523b5d2dc0ff14c913eba8", + "kind": "calls", + "site": { + "file": "src/click/_compat.py", + "startByte": 5399, + "endByte": 5416, + "startLine": 188, + "startColumn": 27, + "endLine": 188, + "endColumn": 44 + }, + "sourceBytes": "_is_binary_reader", + "confidence": [ + "exact" + ], + "frozenSiteMatches": false, + "acceptedAlternateOccurrences": [ + { + "file": "src/click/_compat.py", + "line": 188, + "text": "if buf is not None and _is_binary_reader(buf, True):", + "reason": "Same caller and callee, second direct occurrence; independently source-adjudicated in the earlier panel and reread for this replay. Frozen line 181 remains distinct." + } + ] + } + ], + "responseDiagnostics": [], + "truncated": false, + "classification": "reviewed-call-chain" + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + "src_click_utils_open_file", + "src_click_compat_open_stream", + "src_click_compat_get_binary_stdin", + "src_click_compat_find_binary_reader", + "src_click_compat_is_binary_reader" + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/utils.py", + "line": 422, + "text": "f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)", + "fileSha256": "4720e22c292047ff1a747546b1ba80e96d1d8e8158e2e21ff01cdddb6498db17" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 392, + "text": "return get_binary_stdin(), False", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 320, + "text": "reader = _find_binary_reader(sys.stdin)", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/click/_compat.py", + "line": 181, + "text": "if _is_binary_reader(stream, False):", + "fileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913" + }, + "reviewedSiteSupported": true + } + ], + "renderedLabels": [ + "open_file()", + "open_stream()", + "get_binary_stdin()", + "_find_binary_reader()", + "_is_binary_reader()" + ], + "renderedRelations": [ + "calls", + "calls", + "calls", + "calls" + ], + "classification": "reviewed-call-chain", + "renderedEndpointCandidates": [ + [ + "src_click_utils_open_file" + ], + [ + "src_click_compat_is_binary_reader" + ] + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"isValid\" matched 2 nodes", + "nodeId": null, + "path": null + }, + { + "code": "incomplete_coverage", + "message": "Published graph coverage is incomplete: partial graph published after quarantining 0 nodes and 2 edges with 0 identity collisions; 0 examples omitted by the diagnostic cap", + "nodeId": null, + "path": null + } + ], + "truncated": false, + "classification": "ambiguous" + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "expected exactly one rendered path header" + ], + "nodes": [], + "steps": [], + "classification": "no-path-reported" + }, + { + "question": "redux-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "No single complete path returned." + ], + "nodes": [], + "steps": [], + "responseDiagnostics": [ + { + "code": "ambiguous_match", + "message": "Symbol \"kindOf\" matched 5 nodes", + "nodeId": null, + "path": null + }, + { + "code": "incomplete_coverage", + "message": "Published graph coverage is incomplete: partial graph published after quarantining 0 nodes and 2 edges with 0 identity collisions; 0 examples omitted by the diagnostic cap", + "nodeId": null, + "path": null + } + ], + "truncated": false, + "classification": "ambiguous" + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + "src_utils_kindof_kindof", + "src_utils_kindof_minikindof", + "src_utils_kindof_ctorname" + ], + "steps": [ + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 66, + "text": "typeOfVal = miniKindOf(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + }, + { + "relation": "calls", + "direction": "forward", + "confidence": "EXTRACTED", + "matchingEdges": 1, + "reviewedSite": { + "file": "src/utils/kindOf.ts", + "line": 21, + "text": "const constructorName = ctorName(val)", + "fileSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f" + }, + "reviewedSiteSupported": true + } + ], + "renderedLabels": [ + "kindOf()", + "miniKindOf()", + "ctorName()" + ], + "renderedRelations": [ + "calls", + "calls" + ], + "classification": "reviewed-call-chain", + "renderedEndpointCandidates": [ + [ + "src_utils_kindof_kindof" + ], + [ + "src_utils_kindof_ctorname" + ] + ] + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "sourceSupportedCallPath": true, + "frozenOccurrenceSitesMatch": true, + "failures": [], + "nodes": [ + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "source": { + "file": "src/lib.rs", + "startByte": 29143, + "endByte": 30869, + "startLine": 840, + "startColumn": 4, + "endLine": 882, + "endColumn": 5 + }, + "name": ".handle_entry()" + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "source": { + "file": "src/lib.rs", + "startByte": 34443, + "endByte": 34874, + "startLine": 961, + "startColumn": 4, + "endLine": 971, + "endColumn": 5 + }, + "name": ".follow()" + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "source": { + "file": "src/lib.rs", + "startByte": 34880, + "endByte": 35480, + "startLine": 973, + "startColumn": 4, + "endLine": 989, + "endColumn": 5 + }, + "name": ".check_loop()" + }, + { + "id": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "source": { + "file": "src/error.rs", + "startByte": 6769, + "endByte": 7076, + "startLine": 184, + "startColumn": 4, + "endLine": 196, + "endColumn": 5 + }, + "name": ".from_loop()" + } + ], + "steps": [ + { + "edgeId": "sha256:db51772e6e2a0f689b69f1447740b96e340679a37df7c50bfc727ffe85a06cf4", + "source": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "target": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 29337, + "endByte": 29348, + "startLine": 845, + "startColumn": 25, + "endLine": 845, + "endColumn": 36 + }, + "sourceBytes": "self.follow", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:9d2cbc7e60565250bba8045728a1451893838b7f1b8d26084a542957c88557c3", + "source": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "target": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 34811, + "endByte": 34826, + "startLine": 968, + "startColumn": 12, + "endLine": 968, + "endColumn": 27 + }, + "sourceBytes": "self.check_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + }, + { + "edgeId": "sha256:32f0ce77ab676891029891f1d836f95f3e4c8458dcee2b5d51f7128b8ad32bcf", + "source": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "target": "sha256:4781f5c6bd4c887ab10742a86b44aa5d6164790de28d01e4889788ec345a3e01", + "kind": "calls", + "site": { + "file": "src/lib.rs", + "startByte": 35294, + "endByte": 35310, + "startLine": 981, + "startColumn": 27, + "endLine": 981, + "endColumn": 43 + }, + "sourceBytes": "Error::from_loop", + "confidence": [ + "exact" + ], + "frozenSiteMatches": true, + "acceptedAlternateOccurrences": [] + } + ], + "responseDiagnostics": [], + "truncated": false, + "classification": "reviewed-call-chain" + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "sourceSupportedCallPath": false, + "frozenOccurrenceSitesMatch": false, + "failures": [ + "route length differs from reviewed witness" + ], + "nodes": [], + "steps": [], + "renderedLabels": [ + ".handle_entry()", + ".push()", + ".into_iter()", + "IntoIter", + "WalkDirOptions", + ".fmt()", + "Error", + ".from_loop()" + ], + "renderedRelations": [ + "calls", + "calls", + "references", + "references", + "method", + "references", + "method" + ], + "classification": "mixed-relation-route", + "renderedEndpointCandidates": [ + [ + "src_lib_intoiter_handle_entry" + ], + [ + "src_error_error_from_loop" + ] + ] + } + ], + "baselineDepthDiagnostics": [ + { + "question": "chi-directed-chain", + "maxDepth": 1, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "chi-directed-chain", + "maxDepth": 2, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "chi-directed-chain", + "maxDepth": 3, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 1, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 2, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 3, + "truncated": false, + "diagnostics": [ + "direction_mismatch" + ] + }, + { + "question": "jsoup-directed-chain", + "maxDepth": 1, + "truncated": false, + "diagnostics": [ + "no_match", + "incomplete_coverage" + ] + }, + { + "question": "walkdir-directed-chain", + "maxDepth": 1, + "truncated": false, + "diagnostics": [ + "no_match" + ] + }, + { + "question": "walkdir-directed-chain", + "maxDepth": 2, + "truncated": false, + "diagnostics": [ + "no_match" + ] + } + ], + "finalDepthDiagnostics": [ + { + "question": "chi-directed-chain", + "maxDepth": 1, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "chi-directed-chain", + "maxDepth": 2, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "chi-directed-chain", + "maxDepth": 3, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 1, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 2, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "click-directed-chain", + "maxDepth": 3, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "jsoup-directed-chain", + "maxDepth": 1, + "truncated": true, + "diagnostics": [ + "incomplete_coverage", + "bounded_truncation" + ] + }, + { + "question": "walkdir-directed-chain", + "maxDepth": 1, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + }, + { + "question": "walkdir-directed-chain", + "maxDepth": 2, + "truncated": true, + "diagnostics": [ + "bounded_truncation" + ] + } + ], + "reverseDiagnostics": [ + { + "question": "chi-directed-chain", + "tool": "compass", + "truncated": true, + "diagnostics": [ + "direction_mismatch", + "bounded_truncation" + ] + }, + { + "question": "chi-directed-chain", + "tool": "graphify", + "noPathReported": true + }, + { + "question": "click-directed-chain", + "tool": "compass", + "truncated": false, + "diagnostics": [ + "direction_mismatch" + ] + }, + { + "question": "click-directed-chain", + "tool": "graphify", + "noPathReported": true + }, + { + "question": "jsoup-directed-chain", + "tool": "compass", + "truncated": true, + "diagnostics": [ + "incomplete_coverage", + "bounded_truncation" + ] + }, + { + "question": "jsoup-directed-chain", + "tool": "graphify", + "noPathReported": true + }, + { + "question": "redux-directed-chain", + "tool": "graphify", + "noPathReported": true + }, + { + "question": "walkdir-directed-chain", + "tool": "compass", + "truncated": false, + "diagnostics": [ + "direction_mismatch" + ] + }, + { + "question": "walkdir-directed-chain", + "tool": "graphify", + "noPathReported": true + } + ], + "validation": { + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 4.61 + }, + { + "name": "query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--test", + "code_traversal", + "--test", + "code_explore", + "--test", + "bounded_path_oracle", + "--locked" + ], + "exitCode": 0, + "seconds": 31.12 + }, + { + "name": "cli-query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "code_query_cli", + "--locked" + ], + "exitCode": 0, + "seconds": 55.14 + }, + { + "name": "mcp-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--locked" + ], + "exitCode": 0, + "seconds": 39.03 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 13.57 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 88.2 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 4.04 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.09 + }, + { + "name": "qualification", + "argv": [ + "bash", + "scripts/qualify_code_graph_v1.sh", + "--fixtures-only" + ], + "exitCode": 0, + "seconds": 647.3 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "--locked", + "-p", + "compass-cli", + "--bin", + "compass" + ], + "exitCode": 0, + "seconds": 1.4 + } + ], + "sourceSha256": { + "crates/compass-query/src/code_query.rs": "94afd1d019f2ca1debce79efca5a4d10fd4c211af68eec29446e00c80bb21950", + "crates/compass-query/tests/code_traversal.rs": "d8f49ba8b6019a83ae72d072a8203eb4ebf9bfbe278c4255e43686fca560cea0", + "crates/compass-query/tests/code_explore.rs": "16d2ca185f7a6bd046661d48ad93a28c23e79f3ad8f5b9d5105e382f2e67acce", + "crates/compass-query/tests/bounded_path_oracle.rs": "8ca188956005b813e6f48f473a6c49b88c1c5fd034cf1a0441137b21f279137b", + "crates/compass-cli/tests/code_query_cli.rs": "9f4f30ee26e43182b2afbd6efa10c05e0df3f4c978f0fe91e68a02af885ac96e" + }, + "testCounts": { + "query-tests": { + "passed": 24, + "failed": 0, + "ignored": 0 + }, + "cli-query-tests": { + "passed": 38, + "failed": 0, + "ignored": 0 + }, + "mcp-tests": { + "passed": 60, + "failed": 0, + "ignored": 0 + }, + "workspace-tests": { + "passed": 1106, + "failed": 0, + "ignored": 2 + }, + "product-tests": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "pythonHarnessTests": 137, + "binaryMatchesFinalBuild": true + }, + "artifacts": { + "directed-identity-01/baseline/capture.json": "6dfcfad375d3490cae4227d0237997e410c796882ae1361bd0e353b9ab5511b4", + "directed-identity-01/reproduction.log": "f241f47b339889e6160d6c43fc350bb36f51ed0c972ac808f1d22a5618c6a7cf", + "directed-identity-01/explore-reproduction.log": "f1d8ed9b93150502a8ad625fd5013ea324aad3c04980170172b20940cdf25250", + "directed-identity-02/final/capture.json": "f509b41b5c6d82501141e0ebd1b08733837e61623d827f71c73f03fb33b8482c", + "directed-identity-02/label-control/capture.json": "802575bf7b3d454642a26bb0407bfe14ddf511943b5351211f0d2a46e0db1dcf", + "directed-identity-02/verified-summary.json": "6c7826d5d80e2571e3a83853a357ec1b4e34ca0d7373bdd8a7644fd4d3c61af7", + "directed-identity-02/verify_capture.py": "7118ba4011f88d3aa8554330de70ce2401b4d9ecfa480b26f572a894a17e4240", + "directed-identity-02/verification-attempt-01.log": "4761da703b83adf3a1308117d2ab78739a18a6569e23bd7e6f9a3f0eef491bcc", + "directed-identity-02/verification-02.log": "3864f61f93aba50a49463e79a87ab4559feeb4bb7260e768452cd088ad1d59cc", + "directed-identity-02/collect_label_control.py": "9252482e073c26f417b33427fefed93c14ca37ffc83b42a14e92b2ded5bd940b", + "directed-identity-02/validation.json": "0fb33724f84f968d83b22f3d8038d5305ad9857283f47206102177c84251ae8a", + "directed-identity-02/harness-tests.log": "5a2a4394fc4a1d374cf7b7d222a4cc6b6b84b5e7fd57a38b9811b5af13a96163", + "directed-identity-02/fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "directed-identity-02/query-tests-final.log": "13440ea1b60ce8f7654aa8c8fa6e4ab9ab7f83c19fdfa92cf1431318afe98b8e", + "directed-identity-02/cli-query-tests-final.log": "2b3561cfa14d5b5cfa0f8902ba1f476127de6c9f679b60026d6414b2fbc22d50", + "directed-identity-02/mcp-tests-final.log": "3965ef0908e941988a5b2c684844924099dbe84a3b614a58c3682aecad6f97c8", + "directed-identity-02/clippy-final.log": "2b0bada5d0d43f977a463f9c39b2da3abd390c2b9a10318afced2a7c5cc3d522", + "directed-identity-02/workspace-tests-final.log": "876551b1dceab1f0c410a01374dd3b078282d56bab5709b0975d7bf44531d003", + "directed-identity-02/product-tests-final.log": "75a3eed27aa613ea6b78bd4c0ac613063512fd6de7be93a8d63eb5d64ac5c828", + "directed-identity-02/product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "directed-identity-02/qualification-final.log": "efd8af9231b5c9276338993d2feb77a4d25ba7faa64c45eb0544fcd688632644", + "directed-identity-02/build-final.log": "0999a44a8902a4828631ce72352b8ed5f89f9cb37241b11bc9111026f175e83e" + }, + "limitations": [ + "Same-agent verification, not independent human adjudication.", + "Known development tasks and frozen graphs, not held-out, source precision population, extraction, or latency evidence.", + "Graphify ID arm measures cross-interface endpoint handling as well as path availability; native label control remains separate.", + "Compass Redux endpoint ambiguity remains failure in both arms.", + "Click line 188 is an accepted alternate occurrence, not a frozen-site match.", + "Reverse outputs receive no source-global absence credit.", + "God-object responsibility, explanations, longer walks and fresh held-out qualification remain outstanding.", + "The native depth fix changes incomplete reporting; all four Compass positive ID-path payloads and ten Graphify path payloads were unchanged from baseline.", + "Both compilers and query capture shared a machine; elapsed times are logs, not comparative latency measurements.", + "The verifier first mistook raw and typed evidence JSON schemas as identical; final checks explicitly verify field and anchor projection. Failed attempt retained." + ] +} diff --git a/benchmarks/agent_query/tests/test_directed_identity.py b/benchmarks/agent_query/tests/test_directed_identity.py new file mode 100644 index 000000000..f63c54dc9 --- /dev/null +++ b/benchmarks/agent_query/tests/test_directed_identity.py @@ -0,0 +1,23 @@ +import unittest + +from benchmarks.agent_query.directed_identity import resolver + + +class DirectedIdentityTests(unittest.TestCase): + def test_resolvers_use_only_the_same_public_source_coordinates(self): + witness=dict(file='src/a.rs',line=7,symbol='run',id='oracle-id-must-not-be-used') + c,req=resolver('compass',witness) + self.assertEqual(c,dict(file='src/a.rs',startLine=7,symbol='run')) + self.assertEqual(req,dict(method='search_symbols',arguments=dict(query='run',max_candidates=256,max_nodes=500,max_response_bytes=524288))) + g,req=resolver('graphify',witness) + self.assertEqual(c,g) + self.assertEqual(req,dict(method='get_node',arguments=dict(label='src/a.rs::run'))) + + def test_unsupported_public_delimiter_fails_instead_of_changing_scope(self): + with self.assertRaises(ValueError):resolver('graphify',dict(file='src/a::b.rs',line=7,symbol='run')) + + def test_unknown_tools_fail_explicitly(self): + with self.assertRaises(ValueError):resolver('other',dict(file='a.rs',line=7,symbol='run')) + + +if __name__=='__main__':unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 960b1d70b..7f7a63e34 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2337,6 +2337,97 @@ God-object responsibility judgments, richer source explanations, broader assertion precision, longer directed walks and fresh held-out confirmation remain outstanding. +## Directed endpoint identity and depth-limit correctness + +The registered development replay reuses the five source-reviewed two-to-four +call chains in Chi (Go), Click (Python), jsoup (Java), Redux (TypeScript), and +WalkDir (Rust), with all ten graphs frozen from `rust-index-receiver-03`. +Both tools receive the same endpoint file, start line and symbol. One public +MCP resolver call per endpoint returns candidates; only a unique exact source +match may supply an ID to the directed CLI path request. No graph ID is used to +construct requests. The protocol was committed as `98759ae8`, before capture. +This is source-assisted navigation on known development tasks, not autonomous +endpoint discovery, fresh extraction or held-out evidence. + +A separate native-label control was registered at `2cc02501` after observing +ID-input failures, before executing the control with the final binary. It uses +only the original five short-name pairs, with no retries or candidate changes. +Its results are kept separate: taking the best answer from either arm would +misrepresent both protocols. External deadlines and stream bounds match, but +native internal work limits differ; Graphify exposes no matching CLI depth +control. There is no timing or efficiency ranking. + +| Source-supported directed call chains | Compass | Graphify | +| --- | ---: | ---: | +| Public resolver IDs supplied to CLI | 4/5 | 1/5 | +| Original native short labels | 2/5 | 2/5 | + +The ID workflow resolves four Compass endpoint pairs and all five Graphify +pairs. Compass returns the reviewed chains for Chi, Click, jsoup and WalkDir; +Redux remains unresolved because `search_symbols` returns a function and an +export with the same source line and name. The frozen selector refuses to pick +one. Graphify succeeds for Redux; its Chi route starts at `Mux` rather than the +resolved `MethodFunc` and mixes references/membership with calls. It reports +no path for Click, jsoup and WalkDir with these ID inputs. Inspection of the +pinned Graphify CLI confirms it passes IDs through label scoring without an +exact-ID check. This arm measures endpoint handling across public interfaces +as well as path availability; it is not a pure path-search comparison. + +The native-label control preserves Graphify's Click and Redux successes. +Compass succeeds for Click and WalkDir and refuses three ambiguous requests. +Graphify's Chi and WalkDir routes mix structural relationships with calls; +jsoup reports no directed route. Those structural routes are not credited as +call chains, but this does not establish that the structural edges themselves +are wrong. Every successful chain was checked against exact declarations, +edge orientation, occurrence sites and pinned source. Compass selects a second +valid Click call at line 188 rather than the frozen line 181; literal frozen-site +agreement is therefore 3/5 and 1/5 for its ID and label arms, versus Graphify's +1/5 and 2/5. Conditional static call chains do not imply guaranteed runtime +execution sequences. + +The replay also exposed a correctness defect independent of the paired score: +a failed depth-limited directed search could claim a direction mismatch using +a shorter undirected route, even while a longer forward route existed. A native +regression reproduced that claim before the fix. A second regression reproduced +`explore` dropping incomplete status when no connecting path was returned. +The query layer now checks whether the depth frontier remains open after a +failed bounded search and preserves that status through exploration. Frontier +checks share the work budget and occur after the positive search, so they cannot +consume the budget of a still-viable shorter route. Closed dead ends and cycles +can still prove a complete negative result within the graph. + +All nine registered low-depth requests changed from `truncated: false` to +`truncated: true` with `bounded_truncation`; none now claims `direction_mismatch` +or `no_match`. All four positive Compass ID-path payloads and all ten Graphify +forward/reverse payloads are byte-identical to baseline. The path scores above +were already present before this fix: the improvement is truthful bound reporting. +Reverse probes receive no source-global absence credit. In particular, Chi's +reverse diagnostic combines a closed directed search with an incomplete +undirected search that nevertheless finds a connection; its bounded flag +remains visible. + +A separate same-agent verifier checks saved public request/response transcripts, +source-coordinate selections, CLI arguments, graph/source/binary hashes, full +paths and typed provenance projections. Its first attempt incorrectly required +raw graph and typed query evidence to have identical JSON structure; the retained +failure led to checking their explicit field/anchor projection instead. It is +not independent human adjudication. Raw captures, both native reproductions, +the failed verification attempt and the final verifier are retained under +`directed-identity-01` and `directed-identity-02`; artifact hashes and detailed +outcomes are in `benchmarks/agent_query/directed_identity_development_review.json`. + +Final validation passed: formatting, 24 focused query tests (including the +2,187-request four-node oracle), 38 CLI query tests, 60 MCP tests, workspace +Clippy, 1,106 workspace tests (2 ignored), 9 product tests, product boundary, +complete production fixture qualification and the final CLI build. The Python +harness passed 137 tests. Validated source hashes match implementation +`1ec835bf`; the evaluated binary is byte-identical to the final build. Existing +fixture-omission and compiler warnings remain in the retained logs. No version +or machine-schema bump was made. + +God-object responsibility judgments, richer explanations, broader source +precision, longer walks and fresh held-out confirmation remain unproven. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 4e6ccb252e6b3ff399bc0c1ea0583061d2bacce0 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:18:27 -0700 Subject: [PATCH 66/97] audit: register native responsibility explanation comparison --- ..._explanation_development_registration.json | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 benchmarks/agent_query/native_explanation_development_registration.json diff --git a/benchmarks/agent_query/native_explanation_development_registration.json b/benchmarks/agent_query/native_explanation_development_registration.json new file mode 100644 index 000000000..d93c1f481 --- /dev/null +++ b/benchmarks/agent_query/native_explanation_development_registration.json @@ -0,0 +1,89 @@ +{ + "schema": "compass.native-explanation-development-registration/1", + "scope": "Native explanation and source-coordinate-assisted explanation of five existing responsibility subjects across Go, Python, Java, TypeScript and Rust. Known development tasks; no held-out or population claim.", + "baselineCommit": "47ad5eba99ea2be915d2c786dd1a56597bfb2eb1", + "baselineBinary": "directed-identity-02/compass", + "baselineBinarySha256": "58199f5cfbbce9def3acb58bf019179218c8f6cbe379a3cd96ad9542e8f0b31f", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "graphRun": "rust-index-receiver-03/run.json", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "tasks": [ + { + "repository": "chi", + "symbol": "Mux", + "file": "mux.go", + "startLine": 21, + "question": "Explain how Mux coordinates routing, middleware and subrouters. What state is shared by With?", + "facts": [ + "chi-1", + "chi-2", + "chi-3", + "chi-4" + ] + }, + { + "repository": "click", + "symbol": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 455, + "question": "Explain _AtomicFile ownership, close behavior and context manager cleanup on exceptions.", + "facts": [ + "click-1", + "click-2", + "click-3", + "click-4" + ] + }, + { + "repository": "jsoup", + "symbol": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43, + "question": "Explain Cleaner responsibilities, how it uses the supplied Safelist, and whether cleaning mutates the input document.", + "facts": [ + "jsoup-1", + "jsoup-2", + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "redux", + "symbol": "createStore", + "file": "src/createStore.ts", + "startLine": 86, + "question": "Explain createStore responsibilities and how dispatch and subscriptions share state safely.", + "facts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "walkdir", + "symbol": "IntoIter", + "file": "src/lib.rs", + "startLine": 566, + "question": "Explain IntoIter responsibilities, how it limits open directories, and how it detects symlink loops.", + "facts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + } + ], + "nativeArm": "Each tool gets exactly the supplied short symbol through its public explain CLI, once, with the pinned source repository as working directory. No retries or oracle IDs. All five subjects and 20 facts remain in the denominator.", + "sourceAssistedArm": "Provide the same file/startLine/symbol to one public resolver per subject. Compass search_symbols with limits max_candidates=256,max_nodes=500,max_response_bytes=524288; Graphify get_node file::symbol. Reuse the frozen selected_id source-anchor rule: only one returned matching identity may supply an ID to explain. Function/export collisions remain unresolved; no kind-based or graph-based substitution.", + "commands": "Compass explain SELECTOR --budget 2000 --max-source-bytes 8000 --graph GRAPH; Graphify explain SELECTOR --graph GRAPH. Current directory is the same pinned source root.", + "bounds": "120 seconds and 16 MiB per CLI output stream; 60 seconds and 1 MiB per MCP response. Compass connection budget and source cap have no matching Graphify explain flags. Report actual text/source bytes; no equal-content efficiency or latency claim.", + "judgment": "Judge the same 20 complete implementation facts per arm for explicit native assertions and, separately, sufficient returned source evidence. Labels and related-symbol lists are insufficient. Source excerpts are evidence, not synthesized answers. Preserve incorrect assertions, omitted/partial facts, ambiguity, missing source, limits and every failed request. Validate every displayed source line against the pinned source and verification claim against stored digests. Graph consistency alone is not source precision.", + "sourceControl": "Neither arm gets an external source follow-up. The earlier symmetric 8000-byte source-reading workflow remains a separate comparison (11/20 Compass,13/20 Graphify); do not suppress it or combine best answers into a new score. These native commands expose unequal capabilities; source availability is an outcome, not an equal-I/O claim.", + "provenanceDiagnostic": "Inspection found that Compass marks source digest-verified even when no digest is stored. Reproduce with a native fixture before fixing. Missing, malformed, matching and stale digest cases must distinguish actual verification without rewriting graph/source inputs. This is a Compass correctness diagnostic, not a paired win.", + "limitations": [ + "Same-agent semantic review, not independent adjudication.", + "No model-generated answer or god-object label. Responsibility facts alone do not prove excessive responsibility.", + "Longer walks, broad edge precision, functional community quality and fresh confirmation remain outstanding." + ] +} From 3a58309fca2da62b4f33a1002b2b380d04add6e3 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:28:51 -0700 Subject: [PATCH 67/97] fix(explain): report source verification only for checked digests --- CHANGELOG.md | 4 + COMPATIBILITY.md | 16 ++- SECURITY.md | 10 ++ crates/compass-cli/src/help.rs | 2 +- crates/compass-cli/src/lib.rs | 7 +- crates/compass-cli/tests/code_query_cli.rs | 70 +++++++++++++ crates/compass-query/src/traversal.rs | 35 ++++--- .../compass-query/tests/explanation_source.rs | 97 +++++++++++++++++++ docs/reference/outputs.md | 10 ++ 9 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 crates/compass-query/tests/explanation_source.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d12cad5b..ca1cf5f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Label explanation excerpts without a stored source digest as unverified. + Preserve bounded source access and reject malformed or mismatching digests + instead of claiming that an anchor alone verifies current source. + - Report incomplete directed trail searches when the depth frontier remains unexplored, instead of inferring a direction mismatch from a shorter undirected route. Preserve successful bounded paths and closed negatives; diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 17cc943a1..305aa59ed 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -419,11 +419,17 @@ selected when unique, while multiple modules or declarations remain an explicit ambiguity. This lets path traversal follow the same stored workspace import edges used by `callers`. -`compass explain` now includes a bounded source excerpt by default when the -selected node has a unique source-backed declaration whose file matches the -recorded symbol digest. `--no-source` restores a metadata-only answer; the -existing `--source` flag remains accepted. Ambiguous, unsourced, or changed -files do not produce source text. +`compass explain` includes a bounded source excerpt by default for a uniquely +resolved source-backed declaration. When a stored symbol digest matches the +complete recorded byte span, the excerpt is labeled `digest-verified`. A graph +without that digest can still supply a current excerpt, explicitly labeled +`unverified: no recorded source digest`; an anchor alone cannot establish +freshness. A mismatching or malformed digest prevents source output. This +corrects the earlier unconditional verification label, including text inside +shared JSON output envelopes. The query library exposes `digest_verified` on +`ExplainedSource`; machine schemas and graph artifacts are unchanged. +`--no-source` restores a metadata-only answer; `--source` remains accepted. +Ambiguous or unsourced targets do not produce source text. ### Typed query deadlines diff --git a/SECURITY.md b/SECURITY.md index e710b3fe2..94e8b5e05 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -105,3 +105,13 @@ Inspection, extraction, listing, verification, cache replay, and historical materialization never silently fetch or invoke an arbitrary executable. Model and document cache paths can contain sensitive derived content and should not be attached to public issues. + +## Explanation source verification + +`compass explain` verifies a declaration excerpt against its stored symbol digest +when that digest is present. Verification covers the complete recorded byte span, +including bytes omitted from a truncated excerpt. A missing digest yields an +explicitly unverified current excerpt; a malformed or mismatching digest prevents +source output. File containment, symlink checks and bounded reads apply in both +cases. Digest agreement establishes agreement with the graph's recorded bytes; +it does not authenticate the graph or establish that its semantic claims are true. diff --git a/crates/compass-cli/src/help.rs b/crates/compass-cli/src/help.rs index 8367de58a..355d89e22 100644 --- a/crates/compass-cli/src/help.rs +++ b/crates/compass-cli/src/help.rs @@ -431,7 +431,7 @@ const PAGES: &[Page] = &[ "explain", "Explain a node and its important relationships", ["compass explain [OPTIONS]"], - "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include digest-verified declaration source [default]\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node and verified against its recorded symbol digest. A changed file reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing." + "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include declaration source with verification status [default]\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node. A stored symbol digest must match; missing digests produce an explicitly unverified excerpt. A mismatch or malformed digest reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing." ), page!( "architecture", diff --git a/crates/compass-cli/src/lib.rs b/crates/compass-cli/src/lib.rs index 50ff79d22..142bd8e14 100644 --- a/crates/compass-cli/src/lib.rs +++ b/crates/compass-cli/src/lib.rs @@ -6805,8 +6805,13 @@ fn append_explanation_source( Ok(excerpt) => { output.push_str("\n\nSOURCE "); output.push_str(&excerpt.file); + let verification = if excerpt.digest_verified { + "digest-verified" + } else { + "unverified: no recorded source digest" + }; output.push_str(&format!( - " L{}-L{} (digest-verified)\n", + " L{}-L{} ({verification})\n", excerpt.start_line, excerpt.end_line )); for (offset, line) in excerpt.source.lines().enumerate() { diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 2fdf78c5b..3443422e3 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -2254,3 +2254,73 @@ fn node_command_reports_depth_exhaustion_without_claiming_wrong_direction() } Ok(()) } + +#[test] +fn explain_without_a_stored_digest_never_claims_source_verification() -> Result<(), Box> +{ + let directory = tempfile::tempdir()?; + let source = "fn run() {\n body();\n}\n"; + let source_path = directory.path().join("lib.rs"); + let graph_path = directory.path().join("graph.json"); + std::fs::write( + &graph_path, + serde_json::json!({ + "directed": true, + "multigraph": true, + "nodes": [{ + "id": "n:run", + "kind": "function", + "name": "run", + "source": { + "file": "lib.rs", "startByte": 0, "endByte": source.len(), + "startLine": 1, "endLine": 3, "startColumn": 0, "endColumn": 1 + }, + "details": {"type": "symbol", "data": {"signature": "fn run()"}} + }], + "links": [] + }) + .to_string(), + )?; + // A same-length edit cannot be detected from an anchor alone. + for body in [source.to_owned(), source.replace("body", "next")] { + std::fs::write(&source_path, &body)?; + for format in ["text", "agent-json", "json"] { + let result = run( + Frontend::Compass, + [ + OsString::from("explain"), + OsString::from("n:run"), + OsString::from("--root"), + directory.path().as_os_str().to_owned(), + OsString::from("--graph"), + graph_path.as_os_str().to_owned(), + OsString::from("--format"), + OsString::from(format), + ], + ); + assert_eq!(result.code, 0, "{}", result.stderr); + assert!( + !result.stdout.contains("(digest-verified)"), + "a missing digest cannot verify even a plausible source range: {}", + result.stdout + ); + assert!( + result + .stdout + .contains("unverified: no recorded source digest"), + "{}", + result.stdout + ); + assert!( + result.stdout.contains(if body.contains("next") { + "next();" + } else { + "body();" + }), + "{}", + result.stdout + ); + } + } + Ok(()) +} diff --git a/crates/compass-query/src/traversal.rs b/crates/compass-query/src/traversal.rs index 51d11999a..97df42dca 100644 --- a/crates/compass-query/src/traversal.rs +++ b/crates/compass-query/src/traversal.rs @@ -837,7 +837,7 @@ pub fn render_explanation( } } -/// A digest-verified source excerpt for one uniquely resolved graph node. +/// A bounded source excerpt for one uniquely resolved graph node. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExplainedSource { pub file: String, @@ -845,6 +845,9 @@ pub struct ExplainedSource { pub end_line: u32, pub source: String, pub truncated: bool, + /// True only when the complete recorded span matched a stored digest. + /// An anchor alone does not establish that current source is unchanged. + pub digest_verified: bool, } /// Reasons an explain source excerpt could not be produced. @@ -865,8 +868,8 @@ pub enum ExplanationSourceError { /// Resolution follows the same rules as the explanation renderer: exact /// matches win, source-backed nodes are preferred, and an ambiguous or /// unsourced target is reported instead of guessed. The excerpt is bounded by -/// `max_bytes`, and the recorded symbol digest is verified before the text is -/// returned. +/// `max_bytes`. A recorded symbol digest is verified before text is returned; +/// without one, the excerpt explicitly reports that it is unverified. pub fn explanation_source( graph: &Graph, label: &str, @@ -905,7 +908,7 @@ pub fn explanation_source( let anchor = node_source_anchor(node).ok_or_else(|| ExplanationSourceError::Unsourced { label: label.to_owned(), })?; - let digest = node_source_digest(node); + let digest = node_source_digest(node)?; let span = bounded_source_span( root, &anchor.file, @@ -921,6 +924,7 @@ pub fn explanation_source( end_line: anchor.end_line, source: span.text, truncated: span.truncated, + digest_verified: digest.is_some(), }) } @@ -953,15 +957,22 @@ fn node_source_anchor(node: &NodeRecord) -> Option { }) } -fn node_source_digest(node: &NodeRecord) -> Option { - node.attributes - .get("details")? - .as_object()? - .get("data")? - .as_object()? - .get("sourceDigest")? +fn node_source_digest(node: &NodeRecord) -> Result, ExplanationSourceError> { + let Some(digest) = node + .attributes + .get("details") + .and_then(|details| details.pointer("/data/sourceDigest")) + else { + return Ok(None); + }; + digest .as_str() - .map(str::to_owned) + .map(|value| Some(value.to_owned())) + .ok_or_else(|| { + ExplanationSourceError::Read( + "recorded source digest is not a string; rebuild the graph".to_owned(), + ) + }) } pub fn render_explanation_page( diff --git a/crates/compass-query/tests/explanation_source.rs b/crates/compass-query/tests/explanation_source.rs new file mode 100644 index 000000000..e05604aed --- /dev/null +++ b/crates/compass-query/tests/explanation_source.rs @@ -0,0 +1,97 @@ +use std::error::Error; +use std::fs; + +use compass_model::{Graph, GraphDocument}; +use compass_query::explanation_source; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; + +const SOURCE: &str = "fn run() {\n body();\n}\n"; + +fn graph(digest: Option) -> Result> { + let mut node = json!({ + "id": "n:run", "name": "run", "kind": "function", + "source": { + "file": "lib.rs", "startByte": 0, "endByte": SOURCE.len(), + "startLine": 1, "endLine": 3, "startColumn": 0, "endColumn": 1 + }, + "details": {"type": "symbol", "data": {}} + }); + if let Some(digest) = digest { + node["details"]["data"]["sourceDigest"] = digest; + } + let document: GraphDocument = serde_json::from_value(json!({ + "directed": true, "multigraph": true, "graph": {}, + "nodes": [node], "links": [] + }))?; + Ok(Graph::from_document(document)?) +} + +#[test] +fn missing_digest_returns_explicitly_unverified_current_source() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let graph = graph(None)?; + for source in [SOURCE.to_owned(), SOURCE.replace("body", "next")] { + fs::write(root.path().join("lib.rs"), &source)?; + let excerpt = explanation_source(&graph, "n:run", root.path(), 4096)?; + assert_eq!(excerpt.source, source); + assert!(!excerpt.digest_verified); + assert!(!excerpt.truncated); + let bounded = explanation_source(&graph, "n:run", root.path(), 5)?; + assert_eq!(bounded.source, "fn ru"); + assert!(!bounded.digest_verified); + assert!(bounded.truncated); + } + Ok(()) +} + +#[test] +fn stored_digest_verifies_the_entire_span_even_when_only_a_prefix_is_returned() +-> Result<(), Box> { + let root = tempfile::tempdir()?; + let digest = format!("{:x}", Sha256::digest(SOURCE.as_bytes())); + for digest in [digest.clone(), format!("sha256:{digest}")] { + let graph = graph(Some(json!(digest)))?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let excerpt = explanation_source(&graph, "n:run", root.path(), 5)?; + assert_eq!(excerpt.source, "fn ru"); + assert!(excerpt.digest_verified); + assert!(excerpt.truncated); + fs::write(root.path().join("lib.rs"), SOURCE.replace("body", "next"))?; + let error = explanation_source(&graph, "n:run", root.path(), 5) + .err() + .ok_or("stale source was accepted")?; + assert!(error.to_string().contains("does not match")); + } + Ok(()) +} + +#[test] +fn malformed_digest_cannot_fall_back_to_unverified_source() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + for digest in [ + json!(null), + json!(false), + json!(7), + json!([]), + json!({}), + json!(""), + json!("sha256:bad"), + ] { + let graph = graph(Some(digest))?; + assert!(explanation_source(&graph, "n:run", root.path(), 4096).is_err()); + } + Ok(()) +} + +#[test] +fn absent_digest_keeps_missing_file_and_span_errors() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let graph = graph(None)?; + assert!(explanation_source(&graph, "n:run", root.path(), 4096).is_err()); + fs::write(root.path().join("lib.rs"), "short")?; + assert!(explanation_source(&graph, "n:run", root.path(), 4096).is_err()); + assert!(explanation_source(&graph, "missing", root.path(), 4096).is_err()); + Ok(()) +} diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 5d66667b5..408820617 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -1361,3 +1361,13 @@ Closed dead ends and cycles can return complete negative results; increasing `explore` / `explore_code` also retain incomplete-search status when no connecting path was found; previously that status could be lost with the absent path. + +### Explanation source verification status + +An `explain` source header says `digest-verified` only after the complete recorded +symbol span matches a stored digest. Without a stored digest it says +`unverified: no recorded source digest`; the excerpt is current file content at +the recorded anchor, whose freshness cannot be established. Malformed or +mismatching digests produce `SOURCE unavailable` without source text. Truncating +the returned excerpt does not truncate digest verification. The same status +appears in text carried by shared JSON output envelopes. From bf7bca6ea88f04febddf6253b9a2dcf93d54de2a Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:31:08 -0700 Subject: [PATCH 68/97] audit: compare native responsibility explanations and provenance --- ...native_explanation_development_review.json | 1895 +++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 93 + 2 files changed, 1988 insertions(+) create mode 100644 benchmarks/agent_query/native_explanation_development_review.json diff --git a/benchmarks/agent_query/native_explanation_development_review.json b/benchmarks/agent_query/native_explanation_development_review.json new file mode 100644 index 000000000..51977e1b7 --- /dev/null +++ b/benchmarks/agent_query/native_explanation_development_review.json @@ -0,0 +1,1895 @@ +{ + "schema": "compass.native-explanation-development-review/1", + "scope": "Five existing responsibility subjects across five languages. Native short-label explain and public source-coordinate-assisted explain are separate development arms; neither is held-out, model-generated explanation quality, or god-object detection.", + "registrationCommit": "4e6ccb25", + "registrationSha256": "374dfefbe715ed1a238b46d3b071c8177881e12d3687dbeaa0939fc86dd51b0b", + "implementationCommit": "3a58309fca2da62b4f33a1002b2b380d04add6e3", + "tools": { + "compass": { + "version": "0.3.30", + "binarySha256": "52cf05fe7faacd7f00446694d9e1dbd2244347ee07159755d127e3df961b1a33" + }, + "graphify": { + "version": "0.9.67", + "environmentSha256": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535" + } + }, + "graphs": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "compassGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "graphifyGraphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf" + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "compassGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "graphifyGraphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234" + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "compassGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "graphifyGraphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "compassGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "graphifyGraphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b" + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "compassGraphSha256": "e68fbe798dcf7422184736971dcdfc29d577e1e37269ea9c43456b9f6af54cb3", + "graphifyGraphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd" + } + ], + "summary": { + "native": { + "compass": { + "subjects": 5, + "facts": 20, + "sourceEvidenceFacts": 7, + "explicitNativeFacts": 0, + "resolvedSubjectIdentities": 3, + "stdoutBytes": 24476, + "returnedSourceBytes": 9620 + }, + "graphify": { + "subjects": 5, + "facts": 20, + "sourceEvidenceFacts": 0, + "explicitNativeFacts": 0, + "resolvedSubjectIdentities": 5, + "stdoutBytes": 6286, + "returnedSourceBytes": 0 + } + }, + "source-assisted": { + "compass": { + "subjects": 5, + "facts": 20, + "sourceEvidenceFacts": 7, + "explicitNativeFacts": 0, + "resolvedSubjectIdentities": 4, + "stdoutBytes": 24308, + "returnedSourceBytes": 11448 + }, + "graphify": { + "subjects": 5, + "facts": 20, + "sourceEvidenceFacts": 0, + "explicitNativeFacts": 0, + "resolvedSubjectIdentities": 5, + "stdoutBytes": 6286, + "returnedSourceBytes": 0 + } + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 673, + "stdoutBytes": 4981, + "sourceRows": 28, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "chi-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 242, + "endLine": 262, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 100, + "endLine": 106, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 278, + "endLine": 287, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 476, + "endLine": 493, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + } + ], + "stdoutSha256": "4965b8a889af17ddb2c1b1d056459adf9c8657e4fcd86c2c2f1571f715d910f2", + "identity": "sha256:d003705234e34bbb357969b1c76ccd4cd1adadcf314b380d8ed89255dd633335", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": false, + "sourceLineRange": [ + 21, + 48 + ] + }, + { + "repository": "chi", + "tool": "compass", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 673, + "stdoutBytes": 4981, + "sourceRows": 28, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "chi-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 242, + "endLine": 262, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 100, + "endLine": 106, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 278, + "endLine": 287, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "chi-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 476, + "endLine": 493, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + } + ], + "stdoutSha256": "4965b8a889af17ddb2c1b1d056459adf9c8657e4fcd86c2c2f1571f715d910f2", + "identity": "sha256:d003705234e34bbb357969b1c76ccd4cd1adadcf314b380d8ed89255dd633335", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": false, + "sourceLineRange": [ + 21, + 48 + ] + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1219, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "chi-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 242, + "endLine": 262, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 100, + "endLine": 106, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 278, + "endLine": 287, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 476, + "endLine": 493, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "d4a4072622ae517d2b31ff3f9588e5fdf5e78d878fe937ada18a16d96c4df207", + "identity": "mux_go_chi_mux" + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1219, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "chi-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 242, + "endLine": 262, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 100, + "endLine": 106, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 278, + "endLine": 287, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "chi-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 476, + "endLine": 493, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "d4a4072622ae517d2b31ff3f9588e5fdf5e78d878fe937ada18a16d96c4df207", + "identity": "mux_go_chi_mux" + }, + { + "repository": "click", + "tool": "compass", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 947, + "stdoutBytes": 2306, + "sourceRows": 34, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 4, + "judgments": [ + { + "fact": "click-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 455, + "endLine": 464, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 473, + "endLine": 474, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": true + }, + { + "startLine": 479, + "endLine": 485, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + } + ], + "stdoutSha256": "704d4f4a5b8083a52e1b1ccd7de5b36e286558d26a9719f0871d47ead028a967", + "identity": "sha256:c7f6b2f79e825fa190c8d72b6722557840216c223547cccb255748897a8d91b6", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": false, + "sourceLineRange": [ + 455, + 488 + ] + }, + { + "repository": "click", + "tool": "compass", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 947, + "stdoutBytes": 2306, + "sourceRows": 34, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 4, + "judgments": [ + { + "fact": "click-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 455, + "endLine": 464, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 473, + "endLine": 474, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "click-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": true + }, + { + "startLine": 479, + "endLine": 485, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + } + ], + "stdoutSha256": "704d4f4a5b8083a52e1b1ccd7de5b36e286558d26a9719f0871d47ead028a967", + "identity": "sha256:c7f6b2f79e825fa190c8d72b6722557840216c223547cccb255748897a8d91b6", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": false, + "sourceLineRange": [ + 455, + 488 + ] + }, + { + "repository": "click", + "tool": "graphify", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 749, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "click-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 455, + "endLine": 464, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 473, + "endLine": 474, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": false + }, + { + "startLine": 479, + "endLine": 485, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "ffaa667ce2758665ba1774d5422150e0a91b7c38c555924044caa42bc8b75f87", + "identity": "src_click_compat_atomicfile" + }, + { + "repository": "click", + "tool": "graphify", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 749, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "click-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 455, + "endLine": 464, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 473, + "endLine": 474, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "click-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 466, + "endLine": 471, + "complete": false + }, + { + "startLine": 479, + "endLine": 485, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "ffaa667ce2758665ba1774d5422150e0a91b7c38c555924044caa42bc8b75f87", + "identity": "src_click_compat_atomicfile" + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 8000, + "stdoutBytes": 13225, + "sourceRows": 164, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 3, + "judgments": [ + { + "fact": "jsoup-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 50, + "endLine": 53, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "jsoup-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 62, + "endLine": 70, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "jsoup-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 148, + "endLine": 158, + "complete": true + }, + { + "startLine": 188, + "endLine": 209, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "jsoup-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 124, + "endLine": 133, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + } + ], + "stdoutSha256": "28008e1af1ba8433403e4074ff819d23b7db49ecfe5d76c7758ed9462137a9cd", + "identity": "sha256:e800188397e3d537f837813168a0129fa6e5c7b853657e72bd20769d4d34bab9", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": true, + "sourceLineRange": [ + 43, + 206 + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 8000, + "stdoutBytes": 13225, + "sourceRows": 164, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 3, + "judgments": [ + { + "fact": "jsoup-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 50, + "endLine": 53, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "jsoup-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 62, + "endLine": 70, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + }, + { + "fact": "jsoup-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 148, + "endLine": 158, + "complete": true + }, + { + "startLine": 188, + "endLine": 209, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "jsoup-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": true, + "witnessSupport": [ + { + "startLine": 124, + "endLine": 133, + "complete": true + } + ], + "reason": "Complete source witnesses returned; source evidence is not an authored explanation." + } + ], + "stdoutSha256": "28008e1af1ba8433403e4074ff819d23b7db49ecfe5d76c7758ed9462137a9cd", + "identity": "sha256:e800188397e3d537f837813168a0129fa6e5c7b853657e72bd20769d4d34bab9", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": true, + "sourceLineRange": [ + 43, + 206 + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1185, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "jsoup-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 50, + "endLine": 53, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 62, + "endLine": 70, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 148, + "endLine": 158, + "complete": false + }, + { + "startLine": 188, + "endLine": 209, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 124, + "endLine": 133, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "0514d4f240677e13127202c348e413fa0b31b3d5db54f674fda8a9bf0ad6ac13", + "identity": "src_main_java_org_jsoup_safety_cleaner_cleaner" + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1185, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "jsoup-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 50, + "endLine": 53, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 62, + "endLine": 70, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 148, + "endLine": 158, + "complete": false + }, + { + "startLine": 188, + "endLine": 209, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "jsoup-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 124, + "endLine": 133, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "0514d4f240677e13127202c348e413fa0b31b3d5db54f674fda8a9bf0ad6ac13", + "identity": "src_main_java_org_jsoup_safety_cleaner_cleaner" + }, + { + "repository": "redux", + "tool": "compass", + "arm": "native", + "resolverStatus": "truncated-or-unknown", + "returnedSourceBytes": 0, + "stdoutBytes": 3551, + "sourceRows": 0, + "sourceIdentityVerified": false, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "redux-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 121, + "endLine": 134, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "redux-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 293, + "endLine": 302, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "redux-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 152, + "endLine": 159, + "complete": false + }, + { + "startLine": 221, + "endLine": 243, + "complete": false + }, + { + "startLine": 304, + "endLine": 307, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "redux-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 344, + "endLine": 394, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + } + ], + "stdoutSha256": "b6dbce0ceb5f34ca5f514f18c01671fb9b8278d1ecffff9f3e4081315b9ec148", + "ambiguityReported": true + }, + { + "repository": "redux", + "tool": "compass", + "arm": "source-assisted", + "resolverStatus": "truncated-or-unknown", + "returnedSourceBytes": 0, + "stdoutBytes": 0, + "sourceRows": 0, + "sourceIdentityVerified": false, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "redux-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 121, + "endLine": 134, + "complete": false + } + ], + "reason": "No uniquely selected endpoint; all facts remain in denominator." + }, + { + "fact": "redux-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 293, + "endLine": 302, + "complete": false + } + ], + "reason": "No uniquely selected endpoint; all facts remain in denominator." + }, + { + "fact": "redux-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 152, + "endLine": 159, + "complete": false + }, + { + "startLine": 221, + "endLine": 243, + "complete": false + }, + { + "startLine": 304, + "endLine": 307, + "complete": false + } + ], + "reason": "No uniquely selected endpoint; all facts remain in denominator." + }, + { + "fact": "redux-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 344, + "endLine": 394, + "complete": false + } + ], + "reason": "No uniquely selected endpoint; all facts remain in denominator." + } + ] + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1798, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "redux-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 121, + "endLine": 134, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 293, + "endLine": 302, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 152, + "endLine": 159, + "complete": false + }, + { + "startLine": 221, + "endLine": 243, + "complete": false + }, + { + "startLine": 304, + "endLine": 307, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 344, + "endLine": 394, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "a90b5d7358b5f491398e5c89905499b868d0120977f0acb7a84ece401e024a2c", + "identity": "src_createstore_createstore" + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1798, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "redux-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 121, + "endLine": 134, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 293, + "endLine": 302, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 152, + "endLine": 159, + "complete": false + }, + { + "startLine": 221, + "endLine": 243, + "complete": false + }, + { + "startLine": 304, + "endLine": 307, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "redux-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 344, + "endLine": 394, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "a90b5d7358b5f491398e5c89905499b868d0120977f0acb7a84ece401e024a2c", + "identity": "src_createstore_createstore" + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 413, + "sourceRows": 0, + "sourceIdentityVerified": false, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "walkdir-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 699, + "endLine": 724, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "walkdir-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 901, + "endLine": 911, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "walkdir-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 961, + "endLine": 989, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + }, + { + "fact": "walkdir-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 873, + "endLine": 898, + "complete": false + } + ], + "reason": "Ambiguous subject; no implementation source returned." + } + ], + "stdoutSha256": "c107a1defa0426dd14fee1ab50fcc819423845553306ecc340d425de65af6b24", + "ambiguityReported": true + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 1828, + "stdoutBytes": 3796, + "sourceRows": 41, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "walkdir-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 699, + "endLine": 724, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "walkdir-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 901, + "endLine": 911, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "walkdir-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 961, + "endLine": 989, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + }, + { + "fact": "walkdir-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 873, + "endLine": 898, + "complete": false + } + ], + "reason": "Returned declaration source does not contain every complete witness for this fact." + } + ], + "stdoutSha256": "b01ea850753a993b85d0db8943ea43fcba1cb578284149fdfb2b9ceb0ca0725f", + "identity": "sha256:ffcebdec9953195482aa66714e9b5ff49067552c7fc84d115b397aae9570aac5", + "sourceVerificationClaim": "digest-verified", + "storedDigestMatchesFullSpan": true, + "sourceTruncated": false, + "sourceLineRange": [ + 566, + 606 + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "native", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1335, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "walkdir-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 699, + "endLine": 724, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 901, + "endLine": 911, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 961, + "endLine": 989, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 873, + "endLine": 898, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "8e9ceb3e54f7a2709914e55b29e4d7a19d652deedcec3410913de5ab0d489459", + "identity": "src_lib_intoiter" + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "source-assisted", + "resolverStatus": "resolved", + "returnedSourceBytes": 0, + "stdoutBytes": 1335, + "sourceRows": 0, + "sourceIdentityVerified": true, + "explicitResponsibilityFacts": 0, + "sourceEvidenceFacts": 0, + "judgments": [ + { + "fact": "walkdir-1", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 699, + "endLine": 724, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-2", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 901, + "endLine": 911, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-3", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 961, + "endLine": 989, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + }, + { + "fact": "walkdir-4", + "explicitNativeAssertion": false, + "sufficientSourceEvidence": false, + "witnessSupport": [ + { + "startLine": 873, + "endLine": 898, + "complete": false + } + ], + "reason": "No source excerpt; names, anchors and relationships do not establish this complete implementation fact." + } + ], + "stdoutSha256": "8e9ceb3e54f7a2709914e55b29e4d7a19d652deedcec3410913de5ab0d489459", + "identity": "src_lib_intoiter" + } + ], + "unchangedExplanationPayloads": [ + { + "repository": "chi", + "tool": "compass", + "arm": "native" + }, + { + "repository": "chi", + "tool": "compass", + "arm": "source-assisted" + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "native" + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "source-assisted" + }, + { + "repository": "click", + "tool": "compass", + "arm": "native" + }, + { + "repository": "click", + "tool": "compass", + "arm": "source-assisted" + }, + { + "repository": "click", + "tool": "graphify", + "arm": "native" + }, + { + "repository": "click", + "tool": "graphify", + "arm": "source-assisted" + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "native" + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "source-assisted" + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "native" + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "source-assisted" + }, + { + "repository": "redux", + "tool": "compass", + "arm": "native" + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "native" + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "source-assisted" + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "native" + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "source-assisted" + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "native" + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "source-assisted" + } + ], + "priorSymmetricSourceRead": { + "compassFacts": 11, + "graphifyFacts": 13, + "totalFacts": 20, + "review": "responsibility_source_followup_review_panel_a.json", + "sha256": "4bcba7d92cf4da2ea7cbad2a9a369270cc6ca24b3e51234c943f067966025189", + "scope": "Separate query-plus-source workflow; neither hidden nor combined with native scores." + }, + "realRepositoryDiagnostic": { + "repository": "redux", + "selectedId": "sha256:2dc819f91bc866786968c643e45ad6b3419369536fbc2f8851bb810298489506", + "inputCaptureSha256": "f509b41b5c6d82501141e0ebd1b08733837e61623d827f71c73f03fb33b8482c", + "sourceSha256": "b294e740d5b72819ce5c994b6c2704de2c7ecd908347bc3d21a6b3b70dfd927f", + "graphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "scope": "Post-output Compass provenance diagnostic, not a paired score or a change to the frozen ambiguous-ID selection policy. Select one publicly returned function candidate by explicit kind to inspect its source verification claim. Source checkout and graphs remain untouched.", + "beforeVerificationClaim": "digest-verified", + "afterVerificationClaim": "unverified: no recorded source digest", + "remainingOutputIdentical": true, + "storedDigestAbsent": true + }, + "validation": { + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 6.68 + }, + { + "name": "query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--test", + "explanation_source", + "--locked" + ], + "exitCode": 0, + "seconds": 8.05 + }, + { + "name": "cli-query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "code_query_cli", + "--locked" + ], + "exitCode": 0, + "seconds": 75.48 + }, + { + "name": "mcp-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--locked" + ], + "exitCode": 0, + "seconds": 58.31 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 24.76 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 111.75 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 5.43 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.1 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "--locked", + "-p", + "compass-cli", + "--bin", + "compass" + ], + "exitCode": 0, + "seconds": 1.04 + } + ], + "sourceSha256": { + "crates/compass-query/src/traversal.rs": "c1c50bb65aeecd442c9872ba40657d0ef7338c35d29ddd2453cad1a82ba92720", + "crates/compass-query/tests/explanation_source.rs": "34186c0a032ee576f587d1302d78f07959270ab9e139737414529b7781c869b6", + "crates/compass-cli/src/lib.rs": "11ed108e35f3b2473f87c6b9a9f7e7d20fedd689e3d88a845a8506e7864943b4", + "crates/compass-cli/src/help.rs": "b24eeca41a25c1897b347fe261008265e62d723f7d6932a4efa9d46855929b2b", + "crates/compass-cli/tests/code_query_cli.rs": "24d74e68ea7a5ea8c50590d7c56bf3b5bc48a48a244ed015c8c357492ca35ec0" + }, + "testCounts": { + "query-tests": { + "passed": 4, + "failed": 0, + "ignored": 0 + }, + "cli-query-tests": { + "passed": 39, + "failed": 0, + "ignored": 0 + }, + "mcp-tests": { + "passed": 60, + "failed": 0, + "ignored": 0 + }, + "workspace-tests": { + "passed": 1106, + "failed": 0, + "ignored": 2 + }, + "product-tests": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "binaryMatchesFinalBuild": true, + "notRun": [ + "Extraction/viewer fixture qualification and JavaScript gates were not rerun: this change only affects explanation source-verification status and public CLI text, not extraction, resolution, graph publication or viewer assets.", + "Python benchmark harness was not rerun because no benchmark library changed; new external collectors and the evidence verifier completed successfully." + ] + }, + "artifacts": { + "native-explanation-01/collect.py": "f53104b56a092fd292074bbd1abaa2a89a1295a9c2752812039dedb35aef267a", + "native-explanation-01/baseline/capture.json": "e53cadd1840838b3168ba85b05bcca24d97320ca9a7dab9ef8b0949975f40f88", + "native-explanation-01/reproduction.log": "ecc62e639b793de11c1facb66e192ab5db8f23a88a0d3c3d033003f6ef8b463e", + "native-explanation-02/collect.py": "9ed4c348c6aae2d2e5ac3d8f82c4348f476d954ac1eb9130476a577a51c5c9dd", + "native-explanation-02/final/capture.json": "8a2a89584e06ecaecfec4caedf44de5b959455653bb5a6db15713ee3911a0b17", + "native-explanation-02/verified-summary.json": "247ad0c8139bf6d954785e2a610f889addb23e55b8f8a76dd4cf6e826901ae25", + "native-explanation-02/verify_capture.py": "a8e1b83424c267dad9011626bf39a73740f5993d21f1e743a8275f7efbaa6bfb", + "native-explanation-02/verification.log": "68d345697cecc65ebfad7068a224c39d98e41eddee648197ec41376eeed080a3", + "native-explanation-02/verify_real_digest.py": "bfb122c417a1e231e981b722f6a2077fafeb6de6422c15629db64e1c7b3631b9", + "native-explanation-02/real-digest-diagnostic/capture.json": "7d883ea073ff06de7af6f91a79bb70ec60650593a516fb70219d162b4dc55fb7", + "native-explanation-02/real-digest-diagnostic/before.stdout": "1365900139d869d4120da489e296a7c1b96803bbfaa4cfe62b043361239b1feb", + "native-explanation-02/real-digest-diagnostic/after.stdout": "835f7a9e97c1108ab2590e22177205bde96c8ac6de073e55e349ace03454e18a", + "native-explanation-02/validation.json": "79cbaac0964f808044f01baf3c6ff2d523fcbac522db818e7d99c50ebc59dfc6", + "native-explanation-02/fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "native-explanation-02/query-tests-final.log": "efe962bdf2756c025f78f489697d34db30efdae01ae315e4339fab8b5db86309", + "native-explanation-02/cli-query-tests-final.log": "254b0312f3cff10df323f6b7bd82dd7f9adfcdcb82fa4cd87894f674aa647978", + "native-explanation-02/mcp-tests-final.log": "1d873e89b4ea4691d047357234d9e3298ac19048e96c953f5f35b568f4e3b59a", + "native-explanation-02/clippy-final.log": "de4702a3f15d49659cddb278e4dfcf4b2afee2b8de5eec277006a57a510e151e", + "native-explanation-02/workspace-tests-final.log": "a08c311c95286f6811ea81aa035e4f59e08f1d74b3093455c142644091860676", + "native-explanation-02/product-tests-final.log": "e47d0f2a5347a3c732d1fa2d634bb7b83ad0370cb13b2b1c983e21cd03d23047", + "native-explanation-02/product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "native-explanation-02/build-final.log": "417587ddafc7f98dcabb9a1e2a27c82e820f932ffa72d1bc535b88b7ad1fc735" + }, + "limitations": [ + "Same-agent semantic review and verifier, not independent human adjudication.", + "Only frozen responsibility facts are judged; other printed graph relationships have not all been source-reviewed.", + "Literal complete witnesses supply sufficient evidence for seven facts; no alternate partial-witness credit is used.", + "No model-generated explanations or god-object quality labels.", + "Native capabilities and output sizes differ; this is not equal-I/O efficiency or latency.", + "The earlier query plus symmetric source read remains a separate Graphify advantage (13/20 vs Compass 11/20).", + "The provenance correction does not change these selected native outputs or fact scores: all displayed excerpts already carried matching digests.", + "Graphify resolves all five subjects in each arm. Compass resolves three native subjects and four assisted subjects; the assisted Redux resolver hits its candidate bound.", + "Go Mux and Rust IntoIter declaration excerpts do not include their separately defined methods. The Java 8000-byte excerpt ends before the attribute-write evidence needed for one fact.", + "Native connection budgets/source caps differ. Actual byte totals describe these interfaces, not equal-work performance.", + "God-object judgments, source-based responsibility synthesis, longer walks, wider precision and fresh confirmation remain outstanding." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 7f7a63e34..ec22c41f9 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2428,6 +2428,99 @@ or machine-schema bump was made. God-object responsibility judgments, richer explanations, broader source precision, longer walks and fresh held-out confirmation remain unproven. +## Native responsibility explanations and source verification + +Registration `4e6ccb25` froze a native `explain` comparison for the existing five +responsibility subjects and 20 implementation facts. It reuses the ten graphs +from `rust-index-receiver-03`. Both tools receive the same short subject name +and run in the pinned source checkout. A separate arm supplies the same subject +file, start line and symbol to one public MCP resolver, then passes a unique +source-matched returned ID to `explain`. No graph lookup chooses requests and +there are no retries or external source follow-ups. These are known development +questions, not held-out evaluation or automatic god-object judgments. + +Compass receives a 2,000-token connection budget and an 8,000-byte source cap; +Graphify's native `explain` has no equivalent flags. Both have the same external +120-second timeout and 16 MiB stream caps. Source availability is an observed +capability of these commands, not an equal-I/O or efficiency comparison. + +| Complete responsibility facts in returned evidence | Compass | Graphify | +| --- | ---: | ---: | +| Native short-label `explain` | 7/20 | 0/20 | +| Source-coordinate-assisted `explain` | 7/20 | 0/20 | +| Earlier query plus symmetric bounded source read (separate workflow) | 11/20 | 13/20 | + +Neither native command authors a mechanism explanation: explicit native +responsibility assertions remain **0/20 for both**. Compass's excerpts contain +all four Click `_AtomicFile` facts and three jsoup `Cleaner` facts. Their full +frozen source witnesses are returned, including the otherwise easy-to-misread +Click exception behavior: the `delete` argument is not inspected by `close`. +The Java excerpt ends at a partial line 206, before the attribute write needed +for the remaining cleaning fact; it receives no partial-fact credit. +Graphify returns useful relationships and source anchors, but no source excerpts +sufficient for these complete implementation facts. It can still support an +agent source-reading workflow, as the separate 13/20 result demonstrates. + +Graphify resolves all five subject identities in both arms. Compass resolves +three native subjects and four assisted subjects. Native `createStore` is +ambiguous across 27 source-backed candidates; its assisted search exceeds the +frozen 256-candidate bound and is refused. Native `IntoIter` is ambiguous between +an associated type alias and a struct; the supplied line resolves the struct in +the assisted arm. Go `Mux` and Rust `IntoIter` declaration excerpts include their +fields, but their separately defined methods remain outside those source spans. +Names and method relationships do not establish the missing responsibility +facts. This identifies a concrete next explanation gap: gathering the relevant +implementation evidence beyond the subject's declaration span. + +Native stdout totals are 24,476 bytes for Compass and 6,286 for Graphify; +Compass includes 9,620 raw source bytes. In the assisted arm, the totals are +24,308 and 6,286, with 11,448 Compass source bytes. These totals exclude resolver +traffic, whose raw transcripts are retained. A source excerpt is evidence rather +than an authored answer. Other printed graph relationships have not all received +source review; these fact scores are not full response-precision scores. + +Inspection exposed a separate provenance defect: `explain` labeled every source +excerpt `digest-verified`, including nodes without a stored digest. A native +regression reproduced that label, and checks cover same-length source changes, +matching and mismatching full-span digests, truncated returned prefixes, absent +files, malformed digests and all three CLI output formats. The query layer now +returns an explicit verification flag. A missing digest produces an unverified +current excerpt; malformed or mismatching digests prevent source output. The +existing containment and bounded-read primitives are retained. Verification +matches recorded bytes; it does not authenticate the graph or its semantics. + +A separate post-output Redux diagnostic selects the `kindOf` function from an +already captured public resolver response by its explicit kind and source site. +That diagnostic does **not** change the frozen ambiguous selection policy or any +paired score. The published function has no source digest. Its old explanation +claims verification; the new explanation says `unverified: no recorded source +digest`. All remaining output, graph bytes and source bytes are unchanged. + +All 19 paired explanation payloads and all ten resolver response payloads are +unchanged after the provenance fix: the selected excerpts in the comparison +already had matching digests. Thus the 7/20 evidence result is an observation +about existing native capabilities, not an improvement attributable to this fix. +A separate same-agent verifier checks saved requests/responses, commands, +source/graph/binary hashes, every rendered source line and the full-span digest. +It reuses the frozen resolver helper and separately verifies selected source +anchors; it is not independent human adjudication. + +Implementation `3a58309f` passed formatting, 4 query source tests, 39 CLI query +tests, all 60 MCP tests, workspace Clippy, 1,106 workspace tests (2 ignored), +9 product tests, the product-boundary check and a final CLI build. Validated +source hashes match the commit, and the evaluated binary matches the final +build. Extraction/viewer qualification and JavaScript gates were not rerun: +extraction, resolution, publication and viewer assets are unchanged. No benchmark +library changed, so the Python harness was not rerun; the new external collectors +and evidence verifier completed successfully. The nonfatal macOS linker warning +is retained in the reproduction log. Version remains 0.3.30. + +Detailed judgments and artifact hashes are in +`benchmarks/agent_query/native_explanation_development_review.json`; captures are +under `native-explanation-01` and `native-explanation-02`. God-object diagnosis, +responsibility synthesis, broader source precision, longer walks and fresh +held-out confirmation remain outstanding. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From bac936f9eae3890e23f0776a692c145409134550 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:37:58 -0700 Subject: [PATCH 69/97] audit: register member-source evidence and symmetric source control --- ...ember_source_development_registration.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 benchmarks/agent_query/member_source_development_registration.json diff --git a/benchmarks/agent_query/member_source_development_registration.json b/benchmarks/agent_query/member_source_development_registration.json new file mode 100644 index 000000000..0b9c7344a --- /dev/null +++ b/benchmarks/agent_query/member_source_development_registration.json @@ -0,0 +1,19 @@ +{ + "schema": "compass.member-source-development-registration/1", + "baselineCommit": "bf7bca6ea88f04febddf6253b9a2dcf93d54de2a", + "scope": "Known five-language responsibility subjects and 20 facts; source and prior outputs inspected. Evaluate a new opt-in member-source capability and a separate symmetric public-neighbor/source-window control. No held-out, synthesized explanation, or god-object quality claim.", + "parentRegistrationSha256": "374dfefbe715ed1a238b46d3b071c8177881e12d3687dbeaa0939fc86dd51b0b", + "graphRun": "rust-index-receiver-03/run.json", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "endpointPolicy": "Reuse the ten resolver responses captured under native-explanation-02/final. Apply the same unique file/startLine/symbol selector without changes. Compass Redux remains unavailable after bounded recall; no oracle-ID or kind substitution.", + "nativeMemberArm": "For each uniquely resolved Compass subject, run explain ID --source-members --budget 2000 --max-source-bytes 8000 with the frozen graph and source root. Member mode returns recorded callable implementations through directed containment, including nested type containers, under a shared source-byte budget. Order by source file/start byte/end byte/ID; no question-fact or post-output selection. Missing subjects remain in the denominator. Compare with the existing declaration-only native output; Graphify has no equivalent source-members flag, so do not present this feature delta as a new paired win.", + "symmetricControl": "For both tools, make one public get_neighbors call with the same previously selected root ID, no relation filter. Retain outgoing contains or method rows with source file/start-line anchors. Compass uses explicit returned target anchors; Graphify uses the displayed relation source site, checked afterward against the source declaration. Keep all ambiguities visible; source windows are not identity resolution. Source reads may only use returned anchors.", + "windowPolicy": "Group returned membership anchors by repository-relative file and start line, retaining every matching member row. Sort files and lines. For each group, read from its line start up to the next returned membership anchor in that file, or at most 4096 bytes for the last anchor, whichever comes first. A global 8000-byte payload budget applies, in that order. No next-page requests, retries, fact-guided ranking, end-line advantage or extra source reads. Record partial windows and groups omitted by the byte limit. This is one reproducible agent policy, not a best-possible workflow.", + "bounds": "MCP 60 seconds and 1 MiB per response. CLI 120 seconds and 16 MiB per stdout/stderr. Source files at most 4 MiB, confined beneath pinned read-only repository roots; verify whole-file/source/graph digests before and after. Report response, source payload and command bytes separately, not latency or equal-I/O efficiency.", + "scoring": "Review all 20 complete implementation facts against returned evidence. Exact witness coverage and any separately justified semantic sufficiency are distinct; no partial-fact credit by convenience. Check node identity, membership direction, actual source windows, all errors, ambiguous responses and explicit omissions. Keep the original native 7/20 versus 0/20 and previous symmetric source-read 11/20 versus 13/20 results separate; never construct a best-of-arms score.", + "limitations": [ + "Member source exposes code evidence; it does not itself author a responsibility explanation or diagnose a god object.", + "Full relationship precision, longer paths and fresh confirmation remain outstanding.", + "Same-agent review is not independent human adjudication." + ] +} From 6d4df99470df35f07b19a308311c4020ffe37e60 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 03:59:20 -0700 Subject: [PATCH 70/97] feat(explain): retrieve bounded recorded member implementations --- CHANGELOG.md | 5 + COMPATIBILITY.md | 25 ++ SECURITY.md | 7 + crates/compass-cli/src/help.rs | 2 +- crates/compass-cli/src/lib.rs | 111 +++++-- crates/compass-cli/tests/code_query_cli.rs | 51 +++ .../compass-query/src/explanation_members.rs | 217 ++++++++++++ crates/compass-query/src/lib.rs | 2 + crates/compass-query/src/neighbors.rs | 14 + crates/compass-query/src/traversal.rs | 66 ++-- .../tests/explanation_members.rs | 308 ++++++++++++++++++ docs/reference/outputs.md | 21 ++ 12 files changed, 776 insertions(+), 53 deletions(-) create mode 100644 crates/compass-query/src/explanation_members.rs create mode 100644 crates/compass-query/tests/explanation_members.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ca1cf5f6d..9b0530acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Add opt-in `explain --source-members` to retrieve callable implementations + through recorded containment, including nested types. Share one source-byte + budget, retain individual verification status, and report unavailable or + omitted members explicitly. + - Label explanation excerpts without a stored source digest as unverified. Preserve bounded source access and reject malformed or mismatching digests instead of claiming that an anchor alone verifies current source. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 305aa59ed..69c88819c 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -431,6 +431,31 @@ shared JSON output envelopes. The query library exposes `digest_verified` on `--no-source` restores a metadata-only answer; `--source` remains accepted. Ambiguous or unsourced targets do not produce source text. +### Explanation member source + +`explain --source-members` replaces the declaration excerpt with callable member +excerpts reached through outgoing recorded `contains` / `method` relationships. +It follows nested type containers, preserving recorded direction and parallel +membership evidence in the query library. It does not infer ownership from +names or file proximity, follow calls/references, or promote inferred/deferred +membership. Ambiguous roots remain unresolved and undirected graphs are refused. +Default `explain` behavior is unchanged; `--source-members` conflicts with +`--no-source`. + +Members are ordered by source file, byte range, and exact ID. They share +`--max-source-bytes` (default 4096; maximum 1 MiB in member mode). Discovery has +separate limits of 128 callables, 128 containers, depth 4, 10,000 adjacency +entries and 1 MiB of metadata. Verification attempts share a 16 MiB recorded-span +budget. Discovery-bound failures report unavailable member source; source/work +exhaustion reports truncation and omitted member counts. Individual source +failures remain visible without suppressing other valid excerpts. Stored +source digests and containment checks use the existing source reader. + +This is an additive CLI option and query API, with no graph or shared-output +schema change. The text reports exact member IDs, source anchors, verification +status, retained source bytes, and unavailable/omitted counts. It is structural +source evidence, not a synthesized explanation or a god-object diagnosis. + ### Typed query deadlines `ask`, `search`, `callers`, `callees`, `impact`, `explore`, and `node` accept diff --git a/SECURITY.md b/SECURITY.md index 94e8b5e05..4f3b3789d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -115,3 +115,10 @@ explicitly unverified current excerpt; a malformed or mismatching digest prevent source output. File containment, symlink checks and bounded reads apply in both cases. Digest agreement establishes agreement with the graph's recorded bytes; it does not authenticate the graph or establish that its semantic claims are true. + +Member-source explanations use the same contained source reader for every +excerpt. Discovery, metadata, retained source bytes and verification attempts +have independent bounds. Membership comes from recorded directed structural +relationships; inferred/deferred records, calls and references are excluded. +Source failures and budget exhaustion remain explicit. Missing digests never +become verified merely because an owner-to-member relationship is present. diff --git a/crates/compass-cli/src/help.rs b/crates/compass-cli/src/help.rs index 355d89e22..92d81ca85 100644 --- a/crates/compass-cli/src/help.rs +++ b/crates/compass-cli/src/help.rs @@ -431,7 +431,7 @@ const PAGES: &[Page] = &[ "explain", "Explain a node and its important relationships", ["compass explain [OPTIONS]"], - "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include declaration source with verification status [default]\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node. A stored symbol digest must match; missing digests produce an explicitly unverified excerpt. A mismatch or malformed digest reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing." + "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include declaration source with verification status [default]\n --source-members Show recorded callable members instead of the declaration\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound, shared by members [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node. A stored symbol digest must match; missing digests produce an explicitly unverified excerpt. A mismatch or malformed digest reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing.\n --source-members follows directed recorded containment through nested types, in source order.\n It shares one source-byte budget (maximum 1 MiB); omissions and unavailable members remain explicit." ), page!( "architecture", diff --git a/crates/compass-cli/src/lib.rs b/crates/compass-cli/src/lib.rs index 142bd8e14..3ffb4dcdd 100644 --- a/crates/compass-cli/src/lib.rs +++ b/crates/compass-cli/src/lib.rs @@ -86,8 +86,8 @@ use compass_prs::{ProcessRunner, SystemRunner}; use compass_query::{ DEFAULT_AFFECTED_RELATIONS, DEFAULT_DISCOVERY_TEXT_TOKEN_BUDGET, DEFAULT_PATH_DEPTH_LIMIT, DEFAULT_TEXT_TOKEN_BUDGET, DiscoveryTextPageOptions, TextPageOptions, TraversalMode, - discovery_request_digest, explanation_source, format_affected, format_benchmark, - open as open_code_query, open_with_verified_document, query_graph_text_page, + discovery_request_digest, explanation_member_sources, explanation_source, format_affected, + format_benchmark, open as open_code_query, open_with_verified_document, query_graph_text_page, render_discovery_text_page_with_prefix, render_explanation_page, render_shortest_path_with_limit, run_benchmark, }; @@ -6651,11 +6651,16 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { let mut budget_given = false; let mut page = 1_usize; let mut with_source = true; + let mut with_member_source = false; let mut source_root = std::path::PathBuf::from("."); let mut max_source_bytes = DEFAULT_EXPLAIN_SOURCE_BYTES; let mut index = 1; while index < args.len() { match args[index].as_str() { + "--source-members" => { + with_member_source = true; + index += 1; + } "--source" => { with_source = true; index += 1; @@ -6749,6 +6754,15 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { } } } + if with_member_source && !with_source { + return Outcome::failure("error: --source-members conflicts with --no-source".to_owned()); + } + if with_member_source && (max_source_bytes > 1_048_576 || label.len() > 4096) { + return Outcome::failure( + "error: member source accepts at most 1048576 source bytes and a 4096-byte selector" + .to_owned(), + ); + } if let Err(error) = validate_text_pagination(budget, page) { return Outcome::failure(format!("error: {error}")); } @@ -6774,7 +6788,15 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { Ok(output) => output, Err(error) => return Outcome::failure(format!("error: {error}")), }; - let output = if with_source { + let output = if with_member_source { + append_explanation_member_sources( + output, + &loaded.graph, + label, + &source_root, + max_source_bytes, + ) + } else if with_source { append_explanation_source(output, &loaded.graph, label, &source_root, max_source_bytes) } else { output @@ -6803,26 +6825,7 @@ fn append_explanation_source( ) -> String { match explanation_source(graph, label, root, max_source_bytes) { Ok(excerpt) => { - output.push_str("\n\nSOURCE "); - output.push_str(&excerpt.file); - let verification = if excerpt.digest_verified { - "digest-verified" - } else { - "unverified: no recorded source digest" - }; - output.push_str(&format!( - " L{}-L{} ({verification})\n", - excerpt.start_line, excerpt.end_line - )); - for (offset, line) in excerpt.source.lines().enumerate() { - let number = excerpt.start_line as usize + offset; - output.push_str(&format!(" {number:>6}: {line}\n")); - } - if excerpt.truncated { - output.push_str(&format!( - " [truncated: excerpt limited to {max_source_bytes} bytes; pass --max-source-bytes for more]\n" - )); - } + append_explained_source(&mut output, &excerpt, max_source_bytes); output.trim_end().to_owned() } Err(error) => { @@ -6832,6 +6835,68 @@ fn append_explanation_source( } } +fn append_explained_source( + output: &mut String, + excerpt: &compass_query::ExplainedSource, + max_source_bytes: u64, +) { + output.push_str("\n\nSOURCE "); + output.push_str(&excerpt.file); + let verification = if excerpt.digest_verified { + "digest-verified" + } else { + "unverified: no recorded source digest" + }; + output.push_str(&format!( + " L{}-L{} ({verification})\n", + excerpt.start_line, excerpt.end_line + )); + for (offset, line) in excerpt.source.lines().enumerate() { + let number = excerpt.start_line as usize + offset; + output.push_str(&format!(" {number:>6}: {line}\n")); + } + if excerpt.truncated { + output.push_str(&format!( + " [truncated: excerpt limited to {max_source_bytes} bytes; pass --max-source-bytes for more]\n" + )); + } +} + +fn append_explanation_member_sources( + mut output: String, + graph: &compass_model::Graph, + label: &str, + root: &std::path::Path, + max_source_bytes: u64, +) -> String { + match explanation_member_sources(graph, label, root, max_source_bytes) { + Ok(report) => { + let unavailable = report.members.iter().filter(|m| m.source.is_err()).count(); + output.push_str(&format!( + "\n\nMEMBER SOURCES owner={} retained={} omitted={} unavailable={} source_bytes={} truncated={}\nRecorded containment; source order; shared {}-byte source budget.", + serde_json::json!(report.root.id), report.members.len(), report.omitted_members, + unavailable, report.source_bytes, report.truncated, max_source_bytes, + )); + for member in report.members { + output.push_str(&format!( + "\n\nMEMBER {} {}", + serde_json::json!(member.node.id), + serde_json::json!(member.node.label()) + )); + match member.source { + Ok(excerpt) => { + let retained_bytes = excerpt.source.len() as u64; + append_explained_source(&mut output, &excerpt, retained_bytes); + } + Err(error) => output.push_str(&format!("\nSOURCE unavailable: {error}")), + } + } + } + Err(error) => output.push_str(&format!("\n\nMEMBER SOURCES unavailable: {error}")), + } + output.trim_end().to_owned() +} + fn validate_text_pagination(token_budget: usize, page: usize) -> Result<(), String> { if token_budget == 0 { return Err("token budget must be greater than zero".to_owned()); diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 3443422e3..4498139cf 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -2324,3 +2324,54 @@ fn explain_without_a_stored_digest_never_claims_source_verification() -> Result< } Ok(()) } + +#[test] +fn explain_member_source_reaches_implementations_outside_type_declarations() +-> Result<(), Box> { + use sha2::{Digest, Sha256}; + let directory = tempfile::tempdir()?; + let owner = "struct Owner {}\n"; + let method = "fn work() {\n perform();\n}\n"; + std::fs::write(directory.path().join("lib.rs"), format!("{owner}{method}"))?; + let graph = directory.path().join("graph.json"); + let nodes = [("owner", "Owner", "struct", owner, 0, 1, 1), ("method", "work", "method", method, owner.len(), 2, 4)] + .into_iter().map(|(id, name, kind, text, start, first, last)| serde_json::json!({ + "id": id, "name": name, "kind": kind, + "source": {"file": "lib.rs", "startByte": start, "endByte": start + text.len(), "startLine": first, "endLine": last, "startColumn": 0, "endColumn": 1}, + "details": {"type": "symbol", "data": {"sourceDigest": format!("{:x}", Sha256::digest(text.as_bytes()))}} + })).collect::>(); + std::fs::write(&graph, serde_json::json!({"directed":true,"multigraph":true,"nodes":nodes,"links":[{"source":"owner","target":"method","relation":"contains","confidence":"EXTRACTED"}]}).to_string())?; + let run_command = |extra: &[&str]| { + let mut args = vec![ + OsString::from("explain"), + OsString::from("owner"), + OsString::from("--root"), + directory.path().as_os_str().to_owned(), + OsString::from("--graph"), + graph.as_os_str().to_owned(), + ]; + args.extend(extra.iter().map(OsString::from)); + run(Frontend::Compass, args) + }; + let declaration = run_command(&[]); + assert_eq!(declaration.code, 0, "{}", declaration.stderr); + assert!(!declaration.stdout.contains("perform();")); + for format in ["text", "agent-json", "json"] { + let members = run_command(&["--source-members", "--format", format]); + assert_eq!(members.code, 0, "{}", members.stderr); + assert!(members.stdout.contains("MEMBER SOURCES")); + assert!(members.stdout.contains("perform();")); + assert!(members.stdout.contains("L2-L4 (digest-verified)")); + assert!(!members.stdout.contains("L1-L1 (digest-verified)")); + } + let bounded = run_command(&["--source-members", "--max-source-bytes", "5"]); + assert_eq!(bounded.code, 0, "{}", bounded.stderr); + assert!(bounded.stdout.contains("source_bytes=5 truncated=true")); + assert!(!bounded.stdout.contains("perform();")); + assert_ne!(run_command(&["--source-members", "--no-source"]).code, 0); + assert_ne!( + run_command(&["--source-members", "--max-source-bytes", "1048577"]).code, + 0 + ); + Ok(()) +} diff --git a/crates/compass-query/src/explanation_members.rs b/crates/compass-query/src/explanation_members.rs new file mode 100644 index 000000000..3dac74ccc --- /dev/null +++ b/crates/compass-query/src/explanation_members.rs @@ -0,0 +1,217 @@ +//! Source evidence for recorded members, independent of language layout. + +use std::collections::{BTreeSet, VecDeque}; +use std::path::Path; + +use compass_model::code_graph::NodeKind; +use compass_model::{EdgeRecord, Graph, NodeRecord}; +use serde_json::Value; + +use crate::neighbors::bounded_json_size; +use crate::traversal::{ + ExplainedSource, ExplanationSourceError, explanation_source, node_source_anchor, + resolve_explanation_source_node, +}; + +const MAX_MEMBERS: usize = 128; +const MAX_CONTAINERS: usize = 128; +const MAX_ADJACENCY: usize = 10_000; +const MAX_DEPTH: usize = 4; +const MAX_METADATA_BYTES: usize = 1_048_576; +const MAX_SOURCE_BYTES: u64 = 1_048_576; +const MAX_VERIFIED_SPAN_BYTES: u64 = 16_777_216; + +#[derive(Debug)] +pub struct ExplainedMember<'a> { + pub node: &'a NodeRecord, + pub source: Result, +} + +#[derive(Debug)] +pub struct ExplainedMembers<'a> { + pub root: &'a NodeRecord, + /// Full recorded membership evidence, retaining parallel records. + pub membership: Vec<&'a EdgeRecord>, + pub members: Vec>, + pub omitted_members: usize, + pub source_bytes: u64, + /// Charged span sizes, including failed attempts; not a disk-I/O measurement. + pub verification_bytes_charged: u64, + pub truncated: bool, +} + +fn error(message: &str) -> ExplanationSourceError { + ExplanationSourceError::Read(message.to_owned()) +} + +fn kind(node: &NodeRecord) -> Option { + if node.kind_name().len() > 64 { + return None; + } + serde_json::from_value(Value::String(node.kind_name().to_owned())).ok() +} + +/// Read callable members reached through recorded outgoing containment. +/// +/// Nested type containers are traversed; callable bodies already include their +/// lexical contents, so their nested declarations are not read a second time. +/// Source order is deterministic and all excerpts share one retained-byte cap. +/// Bounds on traversal, metadata and verification work apply independently. +/// This reports graph membership, not proof of excessive responsibility. +pub fn explanation_member_sources<'a>( + graph: &'a Graph, + label: &str, + root: &Path, + max_source_bytes: u64, +) -> Result, ExplanationSourceError> { + if label.len() > 4096 || !(1..=MAX_SOURCE_BYTES).contains(&max_source_bytes) { + return Err(error( + "member source requires a selector up to 4096 bytes and a source budget from 1 to 1048576 bytes", + )); + } + if !graph.is_directed() { + return Err(error( + "member source requires directed recorded containment", + )); + } + let seed = resolve_explanation_source_node(graph, label)?; + let mut metadata_bytes = bounded_json_size(graph.node(seed), MAX_METADATA_BYTES) + .map_err(|_| error("member metadata exceeds its 1048576-byte limit"))?; + let mut queue = VecDeque::from([(seed, 0_usize)]); + let mut containers = BTreeSet::from([seed]); + let mut members = BTreeSet::new(); + let mut membership = Vec::new(); + let mut adjacency = 0_usize; + while let Some((owner, depth)) = queue.pop_front() { + for index in graph.outgoing_edges(owner) { + adjacency += 1; + if adjacency > MAX_ADJACENCY { + return Err(error("member discovery exceeds its 10000-adjacency limit")); + } + let edge = graph.edge(index); + if !matches!(edge.relation(), "contains" | "method") + || edge.source != graph.node(owner).id + { + continue; + } + // Bound normalization even for records later excluded by confidence. + metadata_bytes += + bounded_json_size(edge, MAX_METADATA_BYTES.saturating_sub(metadata_bytes)) + .map_err(|_| error("member metadata exceeds its 1048576-byte limit"))?; + if edge.attributes.get("deferred").and_then(Value::as_bool) == Some(true) + || !matches!(edge.string("confidence").as_str(), "" | "EXTRACTED") + { + continue; + } + let Some(target) = graph.node_index(&edge.target) else { + continue; + }; + let node = graph.node(target); + let Some(kind) = kind(node) else { + continue; + }; + if !kind.is_callable() && !kind.is_type() { + continue; + } + // Canonical order retains every parallel record and unknown attribute. + let mut canonical = serde_json::to_value(edge).map_err(|e| error(&e.to_string()))?; + canonical.sort_all_objects(); + let key = serde_json::to_vec(&canonical).map_err(|e| error(&e.to_string()))?; + membership.push((key, edge)); + if target == seed || containers.contains(&target) || members.contains(&target) { + continue; + } + if depth >= MAX_DEPTH { + return Err(error( + "member discovery exceeds its containment depth limit (4)", + )); + } + metadata_bytes += + bounded_json_size(node, MAX_METADATA_BYTES.saturating_sub(metadata_bytes)) + .map_err(|_| error("member metadata exceeds its 1048576-byte limit"))?; + if kind.is_callable() { + members.insert(target); + if members.len() > MAX_MEMBERS { + return Err(error("member discovery exceeds its 128-callable limit")); + } + } else { + containers.insert(target); + if containers.len() > MAX_CONTAINERS { + return Err(error("member discovery exceeds its 128-container limit")); + } + queue.push_back((target, depth + 1)); + } + } + } + membership.sort_by(|a, b| a.0.cmp(&b.0)); + let mut ordered = members + .into_iter() + .map(|index| { + let node = graph.node(index); + let anchor = node_source_anchor(node); + let key = anchor + .as_ref() + .map(|a| (a.file.clone(), a.start_byte, a.end_byte)); + (key, node) + }) + .collect::>(); + ordered.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.id.cmp(&b.1.id))); + let total = ordered.len(); + let mut report = ExplainedMembers { + root: graph.node(seed), + membership: membership.into_iter().map(|(_, edge)| edge).collect(), + members: Vec::new(), + omitted_members: 0, + source_bytes: 0, + verification_bytes_charged: 0, + truncated: false, + }; + for (_, node) in ordered { + let remaining = max_source_bytes.saturating_sub(report.source_bytes); + if remaining == 0 { + report.truncated = true; + break; + } + let Some(anchor) = node_source_anchor(node) else { + report.members.push(ExplainedMember { + node, + source: Err(ExplanationSourceError::Unsourced { + label: node.id.clone(), + }), + }); + continue; + }; + let Some(span_bytes) = anchor.end_byte.checked_sub(anchor.start_byte) else { + report.members.push(ExplainedMember { + node, + source: Err(error("recorded member source span is inverted")), + }); + continue; + }; + if span_bytes > MAX_VERIFIED_SPAN_BYTES.saturating_sub(report.verification_bytes_charged) { + report.truncated = true; + break; + } + // Charge attempted complete spans too: failed digest checks consume work. + report.verification_bytes_charged += span_bytes; + let mut source = explanation_source(graph, &node.id, root, remaining); + if let Ok(excerpt) = &mut source { + // Lossy UTF-8 decoding can expand a clipped sequence. The retained + // string itself must still fit the shared byte cap. + let cap = usize::try_from(remaining).unwrap_or(usize::MAX); + if excerpt.source.len() > cap { + let mut end = cap; + while !excerpt.source.is_char_boundary(end) { + end -= 1; + } + excerpt.source.truncate(end); + excerpt.truncated = true; + } + report.source_bytes += excerpt.source.len() as u64; + report.truncated |= excerpt.truncated; + } + report.members.push(ExplainedMember { node, source }); + } + report.omitted_members = total.saturating_sub(report.members.len()); + Ok(report) +} diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index ab7c8644d..65a456551 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -9,6 +9,7 @@ mod code_query; mod cql; mod discovery; mod discovery_text; +mod explanation_members; mod export_binding; mod graph_engine; mod index; @@ -38,6 +39,7 @@ pub use discovery_text::{ discovery_response_digest, discovery_result_envelope, render_discovery_text_page, render_discovery_text_page_with_prefix, }; +pub use explanation_members::{ExplainedMember, ExplainedMembers, explanation_member_sources}; pub use graph_engine::{ DirectGraphEngine, EffectiveGraphEngine, GraphEngine, JsonGraphEngine, StoreGraphEngine, open_graph_engine, diff --git a/crates/compass-query/src/neighbors.rs b/crates/compass-query/src/neighbors.rs index 517cca1a0..4829e2a23 100644 --- a/crates/compass-query/src/neighbors.rs +++ b/crates/compass-query/src/neighbors.rs @@ -220,6 +220,20 @@ fn bounded_neighbors<'a>( Ok(report) } +/// Reuse the streaming JSON counter for other bounded graph projections. +pub(crate) fn bounded_json_size( + value: &impl Serialize, + limit: usize, +) -> Result { + let mut counter = ByteCounter { + bytes: 0, + limit, + exceeded: false, + }; + counter.count(value)?; + Ok(counter.bytes) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/compass-query/src/traversal.rs b/crates/compass-query/src/traversal.rs index 97df42dca..fb0bd109e 100644 --- a/crates/compass-query/src/traversal.rs +++ b/crates/compass-query/src/traversal.rs @@ -876,6 +876,35 @@ pub fn explanation_source( root: &Path, max_bytes: u64, ) -> Result { + let node_index = resolve_explanation_source_node(graph, label)?; + let node = graph.node(node_index); + let anchor = node_source_anchor(node).ok_or_else(|| ExplanationSourceError::Unsourced { + label: label.to_owned(), + })?; + let digest = node_source_digest(node)?; + let span = bounded_source_span( + root, + &anchor.file, + anchor.start_byte, + anchor.end_byte, + digest.as_deref(), + max_bytes, + ) + .map_err(|error| ExplanationSourceError::Read(error.to_string()))?; + Ok(ExplainedSource { + file: anchor.file, + start_line: anchor.start_line, + end_line: anchor.end_line, + source: span.text, + truncated: span.truncated, + digest_verified: digest.is_some(), + }) +} + +pub(crate) fn resolve_explanation_source_node( + graph: &Graph, + label: &str, +) -> Result { let exact_matches = find_exact_nodes(graph, label); let mut matches = if exact_matches.is_empty() { find_node(graph, label) @@ -904,40 +933,19 @@ pub fn explanation_source( }); } }; - let node = graph.node(node_index); - let anchor = node_source_anchor(node).ok_or_else(|| ExplanationSourceError::Unsourced { - label: label.to_owned(), - })?; - let digest = node_source_digest(node)?; - let span = bounded_source_span( - root, - &anchor.file, - anchor.start_byte, - anchor.end_byte, - digest.as_deref(), - max_bytes, - ) - .map_err(|error| ExplanationSourceError::Read(error.to_string()))?; - Ok(ExplainedSource { - file: anchor.file, - start_line: anchor.start_line, - end_line: anchor.end_line, - source: span.text, - truncated: span.truncated, - digest_verified: digest.is_some(), - }) + Ok(node_index) } #[derive(Clone, Debug)] -struct NodeSourceAnchor { - file: String, - start_byte: u64, - end_byte: u64, - start_line: u32, - end_line: u32, +pub(crate) struct NodeSourceAnchor { + pub(crate) file: String, + pub(crate) start_byte: u64, + pub(crate) end_byte: u64, + pub(crate) start_line: u32, + pub(crate) end_line: u32, } -fn node_source_anchor(node: &NodeRecord) -> Option { +pub(crate) fn node_source_anchor(node: &NodeRecord) -> Option { let anchor = node.attributes.get("source")?.as_object()?; let file = anchor.get("file")?.as_str()?.to_owned(); let start_byte = anchor.get("startByte")?.as_u64()?; diff --git a/crates/compass-query/tests/explanation_members.rs b/crates/compass-query/tests/explanation_members.rs new file mode 100644 index 000000000..aa41c689c --- /dev/null +++ b/crates/compass-query/tests/explanation_members.rs @@ -0,0 +1,308 @@ +use std::error::Error; +use std::fs; + +use compass_model::{Graph, GraphDocument}; +use compass_query::explanation_member_sources; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; + +const SOURCE: &str = "struct Owner {}\nfn first() { one(); }\nstruct Inner {}\nfn later() { two(); }\nfn wrong() {}\n"; +const FIRST: &str = "fn first() { one(); }"; +const LATER: &str = "fn later() { two(); }"; + +fn node(id: &str, name: &str, kind: &str, text: &str) -> Result> { + let start = SOURCE.find(text).ok_or("missing fixture span")?; + let line = SOURCE[..start].bytes().filter(|b| *b == b'\n').count() + 1; + Ok(json!({ + "id": id, "name": name, "kind": kind, + "source": {"file": "lib.rs", "startByte": start, "endByte": start + text.len(), "startLine": line, "endLine": line, "startColumn": 0, "endColumn": text.len()}, + "details": {"type": "symbol", "data": {"sourceDigest": format!("{:x}", Sha256::digest(text.as_bytes()))}} + })) +} +fn edge(id: &str, source: &str, target: &str, relation: &str) -> Value { + json!({"id": id, "source": source, "target": target, "relation": relation, "confidence": "EXTRACTED", "custom": id}) +} +fn document() -> Result> { + Ok( + json!({"directed": true, "multigraph": true, "graph": {}, "nodes": [ + node("owner", "Owner", "struct", "struct Owner {}")?, + node("first", "first", "method", FIRST)?, + node("inner", "Inner", "class", "struct Inner {}")?, + node("later", "later", "method", LATER)?, + node("wrong", "wrong", "function", "fn wrong() {}")? + ], "links": [ + edge("a", "owner", "first", "contains"), edge("b", "owner", "first", "contains"), + edge("c", "owner", "inner", "contains"), edge("d", "inner", "later", "method"), + edge("e", "owner", "wrong", "calls"), edge("f", "wrong", "owner", "contains"), + edge("g", "inner", "owner", "contains") + ]}), + ) +} +fn graph(value: Value) -> Result> { + Ok(Graph::from_document(serde_json::from_value::< + GraphDocument, + >(value)?)?) +} + +#[test] +fn members_outside_the_owner_span_keep_nested_direction_and_parallel_evidence() +-> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let mut summaries = Vec::new(); + for reversed in [false, true] { + let mut doc = document()?; + if reversed { + doc["nodes"].as_array_mut().ok_or("nodes")?.reverse(); + doc["links"].as_array_mut().ok_or("links")?.reverse(); + } + let graph = graph(doc)?; + let report = explanation_member_sources(&graph, "owner", root.path(), 8000)?; + assert!(!report.truncated); + assert_eq!(report.omitted_members, 0); + assert_eq!( + report + .members + .iter() + .map(|m| m.node.id.as_str()) + .collect::>(), + ["first", "later"] + ); + assert_eq!(report.source_bytes, (FIRST.len() + LATER.len()) as u64); + assert_eq!(report.verification_bytes_charged, report.source_bytes); + let excerpts = report + .members + .iter() + .map(|m| { + m.source + .as_ref() + .map(|s| (s.source.clone(), s.digest_verified)) + }) + .collect::, _>>() + .map_err(|e| e.to_string())?; + assert_eq!( + excerpts, + [(FIRST.to_owned(), true), (LATER.to_owned(), true)] + ); + assert_eq!(report.membership.len(), 5); + assert_eq!( + report + .membership + .iter() + .filter(|e| e.source == "owner" && e.target == "first") + .count(), + 2 + ); + summaries.push( + report + .membership + .iter() + .map(|e| serde_json::to_string(e)) + .collect::, _>>()?, + ); + } + assert_eq!(summaries[0], summaries[1]); + Ok(()) +} + +#[test] +fn source_budget_is_shared_and_omissions_are_explicit() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let graph = graph(document()?)?; + let exact = explanation_member_sources(&graph, "owner", root.path(), FIRST.len() as u64)?; + assert_eq!(exact.members.len(), 1); + assert_eq!(exact.omitted_members, 1); + assert!(exact.truncated); + let partial = explanation_member_sources(&graph, "owner", root.path(), FIRST.len() as u64 + 3)?; + assert_eq!(partial.members.len(), 2); + assert_eq!(partial.omitted_members, 0); + assert!(partial.truncated); + assert_eq!(partial.source_bytes, FIRST.len() as u64 + 3); + assert_eq!( + partial.members[1] + .source + .as_ref() + .map_err(|e| e.to_string())? + .source, + "fn " + ); + assert!( + partial.members[1] + .source + .as_ref() + .map_err(|e| e.to_string())? + .digest_verified + ); + assert_eq!( + partial.verification_bytes_charged, + (FIRST.len() + LATER.len()) as u64 + ); + Ok(()) +} + +#[test] +fn stale_and_unverified_members_keep_their_individual_status() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE.replace("one", "uno"))?; + let mut doc = document()?; + doc["nodes"][3]["details"]["data"] + .as_object_mut() + .ok_or("details")? + .remove("sourceDigest"); + let graph = graph(doc)?; + let report = explanation_member_sources(&graph, "owner", root.path(), 8000)?; + assert!(report.members[0].source.is_err()); + let second = report.members[1] + .source + .as_ref() + .map_err(|e| e.to_string())?; + assert_eq!(second.source, LATER); + assert!(!second.digest_verified); + assert_eq!(report.source_bytes, LATER.len() as u64); + assert_eq!( + report.verification_bytes_charged, + (FIRST.len() + LATER.len()) as u64 + ); + Ok(()) +} + +#[test] +fn ambiguous_roots_and_undirected_graphs_are_not_guessed() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let mut doc = document()?; + doc["nodes"].as_array_mut().ok_or("nodes")?.push(node( + "other", + "Owner", + "struct", + "struct Owner {}", + )?); + let ambiguous = graph(doc)?; + assert!(explanation_member_sources(&ambiguous, "Owner", root.path(), 8000).is_err()); + let mut doc = document()?; + doc["directed"] = json!(false); + assert!(explanation_member_sources(&graph(doc)?, "owner", root.path(), 8000).is_err()); + Ok(()) +} + +#[test] +fn ignored_relations_still_consume_the_discovery_work_budget() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let mut doc = document()?; + doc["links"] = Value::Array( + (0..10001) + .map(|i| edge(&format!("call-{i}"), "owner", "wrong", "calls")) + .collect(), + ); + let graph = graph(doc)?; + let error = explanation_member_sources(&graph, "owner", root.path(), 8000) + .err() + .ok_or("work limit not enforced")?; + assert!(error.to_string().contains("10000-adjacency")); + Ok(()) +} + +#[test] +fn heuristic_and_deferred_members_are_not_promoted_to_recorded_source_members() +-> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let mut doc = document()?; + let mut inferred = edge("inferred", "owner", "wrong", "contains"); + inferred["confidence"] = json!("INFERRED"); + let mut deferred = edge("deferred", "owner", "wrong", "contains"); + deferred["deferred"] = json!(true); + doc["links"] + .as_array_mut() + .ok_or("links")? + .extend([inferred, deferred]); + let graph = graph(doc)?; + let report = explanation_member_sources(&graph, "owner", root.path(), 8000)?; + assert_eq!(report.members.len(), 2); + assert!(report.members.iter().all(|m| m.node.id != "wrong")); + Ok(()) +} + +#[test] +fn metadata_and_parameter_limits_fail_before_source_reads() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let base = graph(document()?)?; + for budget in [0, 1_048_577] { + assert!(explanation_member_sources(&base, "owner", root.path(), budget).is_err()); + } + let mut doc = document()?; + doc["nodes"][0]["large"] = json!("x".repeat(1_048_577)); + assert!(explanation_member_sources(&graph(doc)?, "owner", root.path(), 8000).is_err()); + Ok(()) +} + +#[test] +fn complete_span_work_is_bounded_even_for_failed_source_reads() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let mut doc = document()?; + doc["nodes"][1]["source"]["endByte"] = json!(10_000_000); + doc["nodes"][3]["source"]["endByte"] = json!(10_000_000); + let graph = graph(doc)?; + let report = explanation_member_sources(&graph, "owner", root.path(), 8000)?; + assert_eq!(report.members.len(), 1); + assert!(report.members[0].source.is_err()); + assert_eq!(report.omitted_members, 1); + assert!(report.truncated); + assert_eq!(report.source_bytes, 0); + assert!(report.verification_bytes_charged <= 16_777_216); + Ok(()) +} + +#[test] +fn candidate_and_nesting_caps_are_explicit_errors() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let mut wide = document()?; + wide["links"] = json!([]); + for i in 0..129 { + let id = format!("member-{i}"); + wide["nodes"] + .as_array_mut() + .ok_or("nodes")? + .push(node(&id, &id, "method", FIRST)?); + wide["links"] + .as_array_mut() + .ok_or("links")? + .push(edge(&id, "owner", &id, "contains")); + } + let wide = graph(wide)?; + assert!( + explanation_member_sources(&wide, "owner", root.path(), 8000) + .err() + .ok_or("missing cap")? + .to_string() + .contains("128-callable") + ); + let mut deep = document()?; + deep["links"] = json!([]); + let mut parent = "owner".to_owned(); + for i in 0..5 { + let id = format!("nested-{i}"); + deep["nodes"].as_array_mut().ok_or("nodes")?.push(node( + &id, + &id, + "class", + "struct Inner {}", + )?); + deep["links"] + .as_array_mut() + .ok_or("links")? + .push(edge(&id, &parent, &id, "contains")); + parent = id; + } + let deep = graph(deep)?; + assert!( + explanation_member_sources(&deep, "owner", root.path(), 8000) + .err() + .ok_or("missing cap")? + .to_string() + .contains("depth limit") + ); + Ok(()) +} diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index 408820617..e3e0ab1d8 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -1371,3 +1371,24 @@ the recorded anchor, whose freshness cannot be established. Malformed or mismatching digests produce `SOURCE unavailable` without source text. Truncating the returned excerpt does not truncate digest verification. The same status appears in text carried by shared JSON output envelopes. + +### Member implementation excerpts + +Use `compass explain OWNER --source-members --max-source-bytes 8000` to inspect +recorded callable implementations, including methods defined outside a type's +declaration span. The optional mode replaces the declaration excerpt. It follows +outgoing containment through nested types and orders members by source location; +it does not choose members based on an inferred responsibility. + +`MEMBER SOURCES` reports retained, omitted and unavailable members, total source +bytes, and truncation. Each `MEMBER` has an exact ID followed by its source and +verification status, or an explicit source error. The byte budget is shared +across excerpts; it is not a separate allowance for each member. Discovery and +verification-work limits can also make source unavailable or incomplete. A +complete membership listing establishes what the selected graph records, not +that the source has excessive responsibilities or that every graph edge is true. + +Excerpts use the graph's recorded callable spans. An annotation or decorator +outside those spans, such as Python's `@property`, may be absent even when a +member is fully returned. Use the declaration excerpt when that surrounding +context is needed; member mode does not guarantee more evidence for every fact. From 5131598e0ee06992c12a233656d2816cfd9cf977 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:02:10 -0700 Subject: [PATCH 71/97] audit: compare member evidence with symmetric source windows --- .../member_source_development_review.json | 4607 +++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 95 + 2 files changed, 4702 insertions(+) create mode 100644 benchmarks/agent_query/member_source_development_review.json diff --git a/benchmarks/agent_query/member_source_development_review.json b/benchmarks/agent_query/member_source_development_review.json new file mode 100644 index 000000000..8aef1675b --- /dev/null +++ b/benchmarks/agent_query/member_source_development_review.json @@ -0,0 +1,4607 @@ +{ + "schema": "compass.member-source-development-review/1", + "registration": "benchmarks/agent_query/member_source_development_registration.json", + "registrationSha256": "e95a8b9964a9b5482d66051bc533edb57f93ede59a0a576fb95a85f0460f3f76", + "productCommit": "6d4df99470df35f07b19a308311c4020ffe37e60", + "evaluatedBinarySha256": "25e59141988814e06ea005f14a4380145f18c545d9863f3d477eb0746dfc2693", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "scope": "Five known development subjects; frozen source questions, graphs and public endpoint selection. No held-out evidence or broad superiority claim.", + "summary": { + "native-members": { + "compass": { + "subjects": 5, + "facts": 20, + "resolvedSubjects": 4, + "sourceEvidenceFacts": 13, + "literalWitnessFacts": 4, + "explicitNativeFacts": 0, + "sourceBytes": 23082, + "stdoutBytes": 48177, + "neighborTextBytes": 0, + "neighborWireBytes": 0 + } + }, + "neighbor-window-control": { + "compass": { + "subjects": 5, + "facts": 20, + "resolvedSubjects": 4, + "sourceEvidenceFacts": 11, + "literalWitnessFacts": 10, + "explicitNativeFacts": 0, + "sourceBytes": 27673, + "stdoutBytes": 0, + "neighborTextBytes": 26497, + "neighborWireBytes": 281242 + }, + "graphify": { + "subjects": 5, + "facts": 20, + "resolvedSubjects": 5, + "sourceEvidenceFacts": 15, + "literalWitnessFacts": 14, + "explicitNativeFacts": 0, + "sourceBytes": 35673, + "stdoutBytes": 0, + "neighborTextBytes": 6980, + "neighborWireBytes": 7534 + } + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "arm": "native-members", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 476, + "expected": "\tmethod, ok := methodMap[rctx.RouteMethod]", + "returned": null + }, + { + "line": 477, + "expected": "\tif !ok {", + "returned": null + }, + { + "line": 478, + "expected": "\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)", + "returned": null + }, + { + "line": 479, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 480, + "expected": "\t}", + "returned": null + }, + { + "line": 481, + "expected": "", + "returned": null + }, + { + "line": 482, + "expected": "\t// Find the route", + "returned": null + }, + { + "line": 483, + "expected": "\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {", + "returned": null + }, + { + "line": 484, + "expected": "\t\t// Set http.Request path values from our request context", + "returned": null + }, + { + "line": 485, + "expected": "\t\tfor i, key := range rctx.URLParams.Keys {", + "returned": null + }, + { + "line": 486, + "expected": "\t\t\tvalue := rctx.URLParams.Values[i]", + "returned": null + }, + { + "line": 487, + "expected": "\t\t\tr.SetPathValue(key, value)", + "returned": null + }, + { + "line": 488, + "expected": "\t\t}", + "returned": null + }, + { + "line": 489, + "expected": "\t\tr.Pattern = rctx.RoutePattern()", + "returned": null + }, + { + "line": 490, + "expected": "", + "returned": null + }, + { + "line": 491, + "expected": "\t\th.ServeHTTP(w, r)", + "returned": null + }, + { + "line": 492, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 493, + "expected": "\t}", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 18858, + "members": [ + { + "id": "sha256:0b03f729c4b4c93626eb48b705704c97e8d051ebb76354fdb1028644c0c003f3", + "name": ".ServeHTTP()", + "file": "mux.go", + "startLine": 63, + "lastReturnedLine": 92, + "sourceBytes": 1039, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "name": ".Use()", + "file": "mux.go", + "startLine": 100, + "lastReturnedLine": 105, + "sourceBytes": 225, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b6f4933c875274b7724a57dc037f278cef17603b5a17c0123f74f09c4859ade3", + "name": ".Handle()", + "file": "mux.go", + "startLine": 109, + "lastReturnedLine": 117, + "sourceBytes": 268, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:00488c58ae8e3d2aea91ae763f174cddbcbfa1edb9fda7a9650119778dac1272", + "name": ".HandleFunc()", + "file": "mux.go", + "startLine": 121, + "lastReturnedLine": 123, + "sourceBytes": 104, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "name": ".Method()", + "file": "mux.go", + "startLine": 127, + "lastReturnedLine": 133, + "sourceBytes": 233, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "name": ".MethodFunc()", + "file": "mux.go", + "startLine": 137, + "lastReturnedLine": 139, + "sourceBytes": 120, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c68a79f08a92785f7b855336a541459e9fdc62201d4430b22c5a267720fe0e2e", + "name": ".Connect()", + "file": "mux.go", + "startLine": 143, + "lastReturnedLine": 145, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7eaf9ccc10f79e519444ce15ca5e733334b9706658f775085bcd446e28339098", + "name": ".Delete()", + "file": "mux.go", + "startLine": 149, + "lastReturnedLine": 151, + "sourceBytes": 109, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:cc338ab0fc91b15feee67ab6ef6b5cb5f32500ccb4011e5a672be9f91a09e728", + "name": ".Get()", + "file": "mux.go", + "startLine": 155, + "lastReturnedLine": 157, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f6e8a7560084fdf9a66af0e486fd6ceed76e6607c6ede28fdb1bf8c314ed4a19", + "name": ".Head()", + "file": "mux.go", + "startLine": 161, + "lastReturnedLine": 163, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:86384a3e1b4355bef88f940fa46a5af66a9ba09309b1555884da609ceabad4b5", + "name": ".Options()", + "file": "mux.go", + "startLine": 167, + "lastReturnedLine": 169, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:940225f9f7c63134e90084b0f543639b43293824ada2130d5aa1c37c6e3de01f", + "name": ".Patch()", + "file": "mux.go", + "startLine": 173, + "lastReturnedLine": 175, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:4dc5dca620fc82d5378b46e6646c03877017e141daa1474dab9ce69c1d4cfc56", + "name": ".Post()", + "file": "mux.go", + "startLine": 179, + "lastReturnedLine": 181, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:af7652f3b85f23544717c7555bffe88a5b3ee1fc645491c1dd5d6fd3e5367bbe", + "name": ".Put()", + "file": "mux.go", + "startLine": 185, + "lastReturnedLine": 187, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e2015cff9c7be8cb29614b5a597580ec714c51b3a9bb8622c8a0c15674b79ade", + "name": ".Query()", + "file": "mux.go", + "startLine": 191, + "lastReturnedLine": 193, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8fb9d62ca6fa95670412c09930734152e8175fa59cb9ba6e74c30eba422ed530", + "name": ".Trace()", + "file": "mux.go", + "startLine": 197, + "lastReturnedLine": 199, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:eb197ef3e44ebc05271156e9e59ec93db988e2df4ca08efea5788baa91401518", + "name": ".NotFound()", + "file": "mux.go", + "startLine": 203, + "lastReturnedLine": 219, + "sourceBytes": 419, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1ac18c4f1a692f25a73fdc98b240ed310b2c4a6cf5065ffde2524dd41b7a1f78", + "name": ".MethodNotAllowed()", + "file": "mux.go", + "startLine": 223, + "lastReturnedLine": 239, + "sourceBytes": 467, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "name": ".With()", + "file": "mux.go", + "startLine": 242, + "lastReturnedLine": 263, + "sourceBytes": 682, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0ac374940442cf9a4d0a0d3270546ca4b87d036fd9ec4aa8f8e48ca2d49e4407", + "name": ".Group()", + "file": "mux.go", + "startLine": 268, + "lastReturnedLine": 274, + "sourceBytes": 106, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:395cabe22892d311ed21d29bf7a01cb9d9d2eb1681c634bfd76121bdcf20b34f", + "name": ".Route()", + "file": "mux.go", + "startLine": 278, + "lastReturnedLine": 286, + "sourceBytes": 258, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:29cd93db4ec894393cf257f027b8988a07ee77181191167f3eb2037f2cfdf5be", + "name": ".Mount()", + "file": "mux.go", + "startLine": 295, + "lastReturnedLine": 354, + "sourceBytes": 1986, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0fa43499c8e3e06b415a5fd6585b87c3fd541d6c883379d774c9247edeb94951", + "name": ".Routes()", + "file": "mux.go", + "startLine": 358, + "lastReturnedLine": 360, + "sourceBytes": 60, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:2e266536f77bb3848784eb117cb5935bce2575bba587e1653ce1e6f9c056ebef", + "name": ".Middlewares()", + "file": "mux.go", + "startLine": 363, + "lastReturnedLine": 365, + "sourceBytes": 67, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f0a27beb5e2a6ff88e563c8dbed4dc6b6a697c1895287734f456edcb78d3ada3", + "name": ".Match()", + "file": "mux.go", + "startLine": 373, + "lastReturnedLine": 375, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b84d22d8b0ff377822bcdc4cb0d4aaf092970f4a85c5e1107b4d09b1592c015d", + "name": ".Find()", + "file": "mux.go", + "startLine": 382, + "lastReturnedLine": 408, + "sourceBytes": 537, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1580e9037451fb2ed515953396059772522a762628fd281119ff95ef6389357a", + "name": ".NotFoundHandler()", + "file": "mux.go", + "startLine": 412, + "lastReturnedLine": 417, + "sourceBytes": 138, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6d0c29b15fa70de4383c9685161ae0dea68e92fdbb120770c4a7315e8a2fc10b", + "name": ".MethodNotAllowedHandler()", + "file": "mux.go", + "startLine": 421, + "lastReturnedLine": 422, + "sourceBytes": 116, + "truncated": true, + "digestVerified": true + } + ], + "omittedMembers": 5, + "truncated": true, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "chi", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 476, + "expected": "\tmethod, ok := methodMap[rctx.RouteMethod]", + "returned": null + }, + { + "line": 477, + "expected": "\tif !ok {", + "returned": null + }, + { + "line": 478, + "expected": "\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)", + "returned": null + }, + { + "line": 479, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 480, + "expected": "\t}", + "returned": null + }, + { + "line": 481, + "expected": "", + "returned": null + }, + { + "line": 482, + "expected": "\t// Find the route", + "returned": null + }, + { + "line": 483, + "expected": "\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {", + "returned": null + }, + { + "line": 484, + "expected": "\t\t// Set http.Request path values from our request context", + "returned": null + }, + { + "line": 485, + "expected": "\t\tfor i, key := range rctx.URLParams.Keys {", + "returned": null + }, + { + "line": 486, + "expected": "\t\t\tvalue := rctx.URLParams.Values[i]", + "returned": null + }, + { + "line": 487, + "expected": "\t\t\tr.SetPathValue(key, value)", + "returned": null + }, + { + "line": 488, + "expected": "\t\t}", + "returned": null + }, + { + "line": 489, + "expected": "\t\tr.Pattern = rctx.RoutePattern()", + "returned": null + }, + { + "line": 490, + "expected": "", + "returned": null + }, + { + "line": 491, + "expected": "\t\th.ServeHTTP(w, r)", + "returned": null + }, + { + "line": 492, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 493, + "expected": "\t}", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 13154, + "neighborWireBytes": 147275, + "returnedMembershipRows": 33, + "windows": [ + { + "file": "mux.go", + "startLine": 63, + "lastReturnedLine": 99, + "sourceBytes": 1373, + "partial": false + }, + { + "file": "mux.go", + "startLine": 100, + "lastReturnedLine": 108, + "sourceBytes": 333, + "partial": false + }, + { + "file": "mux.go", + "startLine": 109, + "lastReturnedLine": 120, + "sourceBytes": 386, + "partial": false + }, + { + "file": "mux.go", + "startLine": 121, + "lastReturnedLine": 126, + "sourceBytes": 217, + "partial": false + }, + { + "file": "mux.go", + "startLine": 127, + "lastReturnedLine": 136, + "sourceBytes": 356, + "partial": false + }, + { + "file": "mux.go", + "startLine": 137, + "lastReturnedLine": 142, + "sourceBytes": 241, + "partial": false + }, + { + "file": "mux.go", + "startLine": 143, + "lastReturnedLine": 148, + "sourceBytes": 230, + "partial": false + }, + { + "file": "mux.go", + "startLine": 149, + "lastReturnedLine": 154, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 155, + "lastReturnedLine": 160, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 161, + "lastReturnedLine": 166, + "sourceBytes": 227, + "partial": false + }, + { + "file": "mux.go", + "startLine": 167, + "lastReturnedLine": 172, + "sourceBytes": 228, + "partial": false + }, + { + "file": "mux.go", + "startLine": 173, + "lastReturnedLine": 178, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 179, + "lastReturnedLine": 184, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 185, + "lastReturnedLine": 190, + "sourceBytes": 220, + "partial": false + }, + { + "file": "mux.go", + "startLine": 191, + "lastReturnedLine": 196, + "sourceBytes": 224, + "partial": false + }, + { + "file": "mux.go", + "startLine": 197, + "lastReturnedLine": 202, + "sourceBytes": 242, + "partial": false + }, + { + "file": "mux.go", + "startLine": 203, + "lastReturnedLine": 222, + "sourceBytes": 579, + "partial": false + }, + { + "file": "mux.go", + "startLine": 223, + "lastReturnedLine": 241, + "sourceBytes": 526, + "partial": false + }, + { + "file": "mux.go", + "startLine": 242, + "lastReturnedLine": 267, + "sourceBytes": 880, + "partial": false + }, + { + "file": "mux.go", + "startLine": 268, + "lastReturnedLine": 277, + "sourceBytes": 253, + "partial": false + }, + { + "file": "mux.go", + "startLine": 278, + "lastReturnedLine": 293, + "sourceBytes": 605, + "partial": true + } + ], + "omittedGroups": 12, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 476, + "expected": "\tmethod, ok := methodMap[rctx.RouteMethod]", + "returned": null + }, + { + "line": 477, + "expected": "\tif !ok {", + "returned": null + }, + { + "line": 478, + "expected": "\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)", + "returned": null + }, + { + "line": 479, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 480, + "expected": "\t}", + "returned": null + }, + { + "line": 481, + "expected": "", + "returned": null + }, + { + "line": 482, + "expected": "\t// Find the route", + "returned": null + }, + { + "line": 483, + "expected": "\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {", + "returned": null + }, + { + "line": 484, + "expected": "\t\t// Set http.Request path values from our request context", + "returned": null + }, + { + "line": 485, + "expected": "\t\tfor i, key := range rctx.URLParams.Keys {", + "returned": null + }, + { + "line": 486, + "expected": "\t\t\tvalue := rctx.URLParams.Values[i]", + "returned": null + }, + { + "line": 487, + "expected": "\t\t\tr.SetPathValue(key, value)", + "returned": null + }, + { + "line": 488, + "expected": "\t\t}", + "returned": null + }, + { + "line": 489, + "expected": "\t\tr.Pattern = rctx.RoutePattern()", + "returned": null + }, + { + "line": 490, + "expected": "", + "returned": null + }, + { + "line": 491, + "expected": "\t\th.ServeHTTP(w, r)", + "returned": null + }, + { + "line": 492, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 493, + "expected": "\t}", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 2189, + "neighborWireBytes": 2319, + "returnedMembershipRows": 33, + "windows": [ + { + "file": "mux.go", + "startLine": 63, + "lastReturnedLine": 99, + "sourceBytes": 1373, + "partial": false + }, + { + "file": "mux.go", + "startLine": 100, + "lastReturnedLine": 108, + "sourceBytes": 333, + "partial": false + }, + { + "file": "mux.go", + "startLine": 109, + "lastReturnedLine": 120, + "sourceBytes": 386, + "partial": false + }, + { + "file": "mux.go", + "startLine": 121, + "lastReturnedLine": 126, + "sourceBytes": 217, + "partial": false + }, + { + "file": "mux.go", + "startLine": 127, + "lastReturnedLine": 136, + "sourceBytes": 356, + "partial": false + }, + { + "file": "mux.go", + "startLine": 137, + "lastReturnedLine": 142, + "sourceBytes": 241, + "partial": false + }, + { + "file": "mux.go", + "startLine": 143, + "lastReturnedLine": 148, + "sourceBytes": 230, + "partial": false + }, + { + "file": "mux.go", + "startLine": 149, + "lastReturnedLine": 154, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 155, + "lastReturnedLine": 160, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 161, + "lastReturnedLine": 166, + "sourceBytes": 227, + "partial": false + }, + { + "file": "mux.go", + "startLine": 167, + "lastReturnedLine": 172, + "sourceBytes": 228, + "partial": false + }, + { + "file": "mux.go", + "startLine": 173, + "lastReturnedLine": 178, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 179, + "lastReturnedLine": 184, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 185, + "lastReturnedLine": 190, + "sourceBytes": 220, + "partial": false + }, + { + "file": "mux.go", + "startLine": 191, + "lastReturnedLine": 196, + "sourceBytes": 224, + "partial": false + }, + { + "file": "mux.go", + "startLine": 197, + "lastReturnedLine": 202, + "sourceBytes": 242, + "partial": false + }, + { + "file": "mux.go", + "startLine": 203, + "lastReturnedLine": 222, + "sourceBytes": 579, + "partial": false + }, + { + "file": "mux.go", + "startLine": 223, + "lastReturnedLine": 241, + "sourceBytes": 526, + "partial": false + }, + { + "file": "mux.go", + "startLine": 242, + "lastReturnedLine": 267, + "sourceBytes": 880, + "partial": false + }, + { + "file": "mux.go", + "startLine": 268, + "lastReturnedLine": 277, + "sourceBytes": 253, + "partial": false + }, + { + "file": "mux.go", + "startLine": 278, + "lastReturnedLine": 293, + "sourceBytes": 605, + "partial": true + } + ], + "omittedGroups": 12, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "click", + "tool": "compass", + "arm": "native-members", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 455, + "expected": "class _AtomicFile:", + "returned": null + }, + { + "line": 461, + "expected": "", + "returned": null + }, + { + "line": 462, + "expected": " @property", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Initialization and name getter bodies are present, but the recorded method spans omit @property. A method named name does not establish property semantics; deny the complete fact." + }, + { + "fact": "click-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 874, + "stdoutBytes": 3408, + "members": [ + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "name": ".__init__()", + "file": "src/click/_compat.py", + "startLine": 456, + "lastReturnedLine": 460, + "sourceBytes": 216, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e878c6438bdce958c03a089d80b07294ed83ae9ee2f0f0a633df5fbf50ae9e52", + "name": ".name()", + "file": "src/click/_compat.py", + "startLine": 463, + "lastReturnedLine": 464, + "sourceBytes": 57, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "name": ".close()", + "file": "src/click/_compat.py", + "startLine": 466, + "lastReturnedLine": 471, + "sourceBytes": 200, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5bb38e0f070dd04a772efc8c9ee16003caa1e687bc2c12cc19b86bef7e08349e", + "name": ".__getattr__()", + "file": "src/click/_compat.py", + "startLine": 473, + "lastReturnedLine": 474, + "sourceBytes": 80, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bc6d909e3fad7e2cbc5726cdd962e543d80d2c22e04f4748a0c4a6a6df90301b", + "name": ".__enter__()", + "file": "src/click/_compat.py", + "startLine": 476, + "lastReturnedLine": 477, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4", + "name": ".__exit__()", + "file": "src/click/_compat.py", + "startLine": 479, + "lastReturnedLine": 485, + "sourceBytes": 211, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1b6882765c798cf51eff1c70453ffc9687e893d38733a758d193bd1e6c7845f1", + "name": ".__repr__()", + "file": "src/click/_compat.py", + "startLine": 487, + "lastReturnedLine": 488, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + } + ], + "omittedMembers": 0, + "truncated": false, + "literalWitnessFacts": 0, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "click", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 455, + "expected": "class _AtomicFile:", + "returned": null + } + ], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Only the class header is absent. The exact selected owner is already verified as _AtomicFile, while initialization, @property, and the complete name getter are returned." + }, + { + "fact": "click-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 3673, + "stdoutBytes": 0, + "neighborTextBytes": 2092, + "neighborWireBytes": 23086, + "returnedMembershipRows": 7, + "windows": [ + { + "file": "src/click/_compat.py", + "startLine": 456, + "lastReturnedLine": 462, + "sourceBytes": 236, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 463, + "lastReturnedLine": 465, + "sourceBytes": 63, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 466, + "lastReturnedLine": 472, + "sourceBytes": 206, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 473, + "lastReturnedLine": 475, + "sourceBytes": 86, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 476, + "lastReturnedLine": 478, + "sourceBytes": 61, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 479, + "lastReturnedLine": 486, + "sourceBytes": 217, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 487, + "lastReturnedLine": 590, + "sourceBytes": 2804, + "partial": false + } + ], + "omittedGroups": 0, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 4, + "explicitNativeFacts": 0 + }, + { + "repository": "click", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 455, + "expected": "class _AtomicFile:", + "returned": null + } + ], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Only the class header is absent. The exact selected owner is already verified as _AtomicFile, while initialization, @property, and the complete name getter are returned." + }, + { + "fact": "click-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "click-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 3673, + "stdoutBytes": 0, + "neighborTextBytes": 636, + "neighborWireBytes": 735, + "returnedMembershipRows": 7, + "windows": [ + { + "file": "src/click/_compat.py", + "startLine": 456, + "lastReturnedLine": 462, + "sourceBytes": 236, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 463, + "lastReturnedLine": 465, + "sourceBytes": 63, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 466, + "lastReturnedLine": 472, + "sourceBytes": 206, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 473, + "lastReturnedLine": 475, + "sourceBytes": 86, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 476, + "lastReturnedLine": 478, + "sourceBytes": 61, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 479, + "lastReturnedLine": 486, + "sourceBytes": 217, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 487, + "lastReturnedLine": 590, + "sourceBytes": 2804, + "partial": false + } + ], + "omittedGroups": 0, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 4, + "explicitNativeFacts": 0 + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "native-members", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 6208, + "stdoutBytes": 12866, + "members": [ + { + "id": "sha256:66aaadb3479119cae227c0e51b5674eae36cb9b29e80428e2697aa7cb45518f0", + "name": "", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 50, + "lastReturnedLine": 53, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "name": ".clean()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "lastReturnedLine": 70, + "sourceBytes": 319, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:817144c6469132780623c859650f3dbb59eef49b75108e12e8fd2efe7763b330", + "name": ".isValid()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 94, + "lastReturnedLine": 101, + "sourceBytes": 441, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "name": ".isValidBodyHtml()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 124, + "lastReturnedLine": 133, + "sourceBytes": 630, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d82578355e71b0d55f68499706f70276db9cf44a16bf283bba6360df410deec5", + "name": "", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 143, + "lastReturnedLine": 146, + "sourceBytes": 144, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:65912eb3660f15092355589775d874a7a702efcf0336e9865cf93b4fb56aaa29", + "name": ".head()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 148, + "lastReturnedLine": 173, + "sourceBytes": 1371, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:fb8588d79c5d9dcbd1a0c6a8e2f901d5c54be3050e7626a3b8f59a8c55bd025c", + "name": ".tail()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 175, + "lastReturnedLine": 179, + "sourceBytes": 266, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "name": ".copySafeNodes()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "lastReturnedLine": 186, + "sourceBytes": 227, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7ce3db44fb899d894dd900ab8808bd0fdc411323a6276f4164fede39e46f8157", + "name": ".createSafeElement()", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 188, + "lastReturnedLine": 235, + "sourceBytes": 2553, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bd66755ef9e0f349f3e772c8d9b23a0441194bd6300a6431d51bc2f11650e890", + "name": "", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 241, + "lastReturnedLine": 244, + "sourceBytes": 146, + "truncated": false, + "digestVerified": true + } + ], + "omittedMembers": 0, + "truncated": false, + "literalWitnessFacts": 0, + "sourceEvidenceFacts": 4, + "explicitNativeFacts": 0 + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 206, + "expected": " Range.AttributeRange range = sourceAttrs.sourceRange(key);", + "returned": " Range.AttributeRange r" + }, + { + "line": 207, + "expected": " destAttrs.put(key, value);", + "returned": null + }, + { + "line": 208, + "expected": " NodeInternals.attributeRange(destAttrs, key, range);", + "returned": null + }, + { + "line": 209, + "expected": " } else", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 7014, + "neighborWireBytes": 66437, + "returnedMembershipRows": 9, + "windows": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 44, + "lastReturnedLine": 49, + "sourceBytes": 180, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 50, + "lastReturnedLine": 61, + "sourceBytes": 541, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "lastReturnedLine": 93, + "sourceBytes": 1549, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 94, + "lastReturnedLine": 123, + "sourceBytes": 1625, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 124, + "lastReturnedLine": 137, + "sourceBytes": 748, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 138, + "lastReturnedLine": 181, + "sourceBytes": 2032, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "lastReturnedLine": 187, + "sourceBytes": 233, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 188, + "lastReturnedLine": 206, + "sourceBytes": 1092, + "partial": true + } + ], + "omittedGroups": 1, + "literalWitnessFacts": 3, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1034, + "neighborWireBytes": 1135, + "returnedMembershipRows": 8, + "windows": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 50, + "lastReturnedLine": 61, + "sourceBytes": 541, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "lastReturnedLine": 93, + "sourceBytes": 1549, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 94, + "lastReturnedLine": 123, + "sourceBytes": 1625, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 124, + "lastReturnedLine": 137, + "sourceBytes": 748, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 138, + "lastReturnedLine": 181, + "sourceBytes": 2032, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "lastReturnedLine": 187, + "sourceBytes": 233, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 188, + "lastReturnedLine": 210, + "sourceBytes": 1272, + "partial": true + } + ], + "omittedGroups": 1, + "literalWitnessFacts": 4, + "sourceEvidenceFacts": 4, + "explicitNativeFacts": 0 + }, + { + "repository": "redux", + "tool": "compass", + "arm": "native-members", + "resolverStatus": "truncated-or-unknown", + "judgments": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 121, + "expected": " if (typeof enhancer !== 'undefined') {", + "returned": null + }, + { + "line": 122, + "expected": " if (typeof enhancer !== 'function') {", + "returned": null + }, + { + "line": 123, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 124, + "expected": " `Expected the enhancer to be a function. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 125, + "expected": " enhancer", + "returned": null + }, + { + "line": 126, + "expected": " )}'`", + "returned": null + }, + { + "line": 127, + "expected": " )", + "returned": null + }, + { + "line": 128, + "expected": " }", + "returned": null + }, + { + "line": 129, + "expected": "", + "returned": null + }, + { + "line": 130, + "expected": " return enhancer(createStore)(", + "returned": null + }, + { + "line": 131, + "expected": " reducer,", + "returned": null + }, + { + "line": 132, + "expected": " preloadedState as PreloadedState | undefined", + "returned": null + }, + { + "line": 133, + "expected": " )", + "returned": null + }, + { + "line": 134, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 293, + "expected": " if (isDispatching) {", + "returned": null + }, + { + "line": 294, + "expected": " throw new Error('Reducers may not dispatch actions.')", + "returned": null + }, + { + "line": 295, + "expected": " }", + "returned": null + }, + { + "line": 296, + "expected": "", + "returned": null + }, + { + "line": 297, + "expected": " try {", + "returned": null + }, + { + "line": 298, + "expected": " isDispatching = true", + "returned": null + }, + { + "line": 299, + "expected": " currentState = currentReducer(currentState, action)", + "returned": null + }, + { + "line": 300, + "expected": " } finally {", + "returned": null + }, + { + "line": 301, + "expected": " isDispatching = false", + "returned": null + }, + { + "line": 302, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 152, + "expected": " function ensureCanMutateNextListeners() {", + "returned": null + }, + { + "line": 153, + "expected": " if (nextListeners === currentListeners) {", + "returned": null + }, + { + "line": 154, + "expected": " nextListeners = new Map()", + "returned": null + }, + { + "line": 155, + "expected": " currentListeners.forEach((listener, key) => {", + "returned": null + }, + { + "line": 156, + "expected": " nextListeners.set(key, listener)", + "returned": null + }, + { + "line": 157, + "expected": " })", + "returned": null + }, + { + "line": 158, + "expected": " }", + "returned": null + }, + { + "line": 159, + "expected": " }", + "returned": null + }, + { + "line": 221, + "expected": " ensureCanMutateNextListeners()", + "returned": null + }, + { + "line": 222, + "expected": " const listenerId = listenerIdCounter++", + "returned": null + }, + { + "line": 223, + "expected": " nextListeners.set(listenerId, listener)", + "returned": null + }, + { + "line": 224, + "expected": "", + "returned": null + }, + { + "line": 225, + "expected": " return function unsubscribe() {", + "returned": null + }, + { + "line": 226, + "expected": " if (!isSubscribed) {", + "returned": null + }, + { + "line": 227, + "expected": " return", + "returned": null + }, + { + "line": 228, + "expected": " }", + "returned": null + }, + { + "line": 229, + "expected": "", + "returned": null + }, + { + "line": 230, + "expected": " if (isDispatching) {", + "returned": null + }, + { + "line": 231, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 232, + "expected": " 'You may not unsubscribe from a store listener while the reducer is executing. ' +", + "returned": null + }, + { + "line": 233, + "expected": " 'See https://redux.js.org/api/store#subscribelistener for more details.'", + "returned": null + }, + { + "line": 234, + "expected": " )", + "returned": null + }, + { + "line": 235, + "expected": " }", + "returned": null + }, + { + "line": 236, + "expected": "", + "returned": null + }, + { + "line": 237, + "expected": " isSubscribed = false", + "returned": null + }, + { + "line": 238, + "expected": "", + "returned": null + }, + { + "line": 239, + "expected": " ensureCanMutateNextListeners()", + "returned": null + }, + { + "line": 240, + "expected": " nextListeners.delete(listenerId)", + "returned": null + }, + { + "line": 241, + "expected": " currentListeners = null", + "returned": null + }, + { + "line": 242, + "expected": " }", + "returned": null + }, + { + "line": 243, + "expected": " }", + "returned": null + }, + { + "line": 304, + "expected": " const listeners = (currentListeners = nextListeners)", + "returned": null + }, + { + "line": 305, + "expected": " listeners.forEach(listener => {", + "returned": null + }, + { + "line": 306, + "expected": " listener()", + "returned": null + }, + { + "line": 307, + "expected": " })", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 344, + "expected": " function observable() {", + "returned": null + }, + { + "line": 345, + "expected": " const outerSubscribe = subscribe", + "returned": null + }, + { + "line": 346, + "expected": " return {", + "returned": null + }, + { + "line": 347, + "expected": " /**", + "returned": null + }, + { + "line": 348, + "expected": " * The minimal observable subscription method.", + "returned": null + }, + { + "line": 349, + "expected": " * @param observer Any object that can be used as an observer.", + "returned": null + }, + { + "line": 350, + "expected": " * The observer object should have a `next` method.", + "returned": null + }, + { + "line": 351, + "expected": " * @returns An object with an `unsubscribe` method that can", + "returned": null + }, + { + "line": 352, + "expected": " * be used to unsubscribe the observable from the store, and prevent further", + "returned": null + }, + { + "line": 353, + "expected": " * emission of values from the observable.", + "returned": null + }, + { + "line": 354, + "expected": " */", + "returned": null + }, + { + "line": 355, + "expected": " subscribe(observer: unknown) {", + "returned": null + }, + { + "line": 356, + "expected": " if (typeof observer !== 'object' || observer === null) {", + "returned": null + }, + { + "line": 357, + "expected": " throw new TypeError(", + "returned": null + }, + { + "line": 358, + "expected": " `Expected the observer to be an object. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 359, + "expected": " observer", + "returned": null + }, + { + "line": 360, + "expected": " )}'`", + "returned": null + }, + { + "line": 361, + "expected": " )", + "returned": null + }, + { + "line": 362, + "expected": " }", + "returned": null + }, + { + "line": 363, + "expected": "", + "returned": null + }, + { + "line": 364, + "expected": " function observeState() {", + "returned": null + }, + { + "line": 365, + "expected": " const observerAsObserver = observer as Observer", + "returned": null + }, + { + "line": 366, + "expected": " if (observerAsObserver.next) {", + "returned": null + }, + { + "line": 367, + "expected": " observerAsObserver.next(getState())", + "returned": null + }, + { + "line": 368, + "expected": " }", + "returned": null + }, + { + "line": 369, + "expected": " }", + "returned": null + }, + { + "line": 370, + "expected": "", + "returned": null + }, + { + "line": 371, + "expected": " observeState()", + "returned": null + }, + { + "line": 372, + "expected": " const unsubscribe = outerSubscribe(observeState)", + "returned": null + }, + { + "line": 373, + "expected": " return { unsubscribe }", + "returned": null + }, + { + "line": 374, + "expected": " },", + "returned": null + }, + { + "line": 375, + "expected": "", + "returned": null + }, + { + "line": 376, + "expected": " [$$observable]() {", + "returned": null + }, + { + "line": 377, + "expected": " return this", + "returned": null + }, + { + "line": 378, + "expected": " }", + "returned": null + }, + { + "line": 379, + "expected": " }", + "returned": null + }, + { + "line": 380, + "expected": " }", + "returned": null + }, + { + "line": 381, + "expected": "", + "returned": null + }, + { + "line": 382, + "expected": " // When a store is created, an \"INIT\" action is dispatched so that every", + "returned": null + }, + { + "line": 383, + "expected": " // reducer returns their initial state. This effectively populates", + "returned": null + }, + { + "line": 384, + "expected": " // the initial state tree.", + "returned": null + }, + { + "line": 385, + "expected": " dispatch({ type: ActionTypes.INIT } as A)", + "returned": null + }, + { + "line": 386, + "expected": "", + "returned": null + }, + { + "line": 387, + "expected": " const store = {", + "returned": null + }, + { + "line": 388, + "expected": " dispatch: dispatch as Dispatch,", + "returned": null + }, + { + "line": 389, + "expected": " subscribe,", + "returned": null + }, + { + "line": 390, + "expected": " getState,", + "returned": null + }, + { + "line": 391, + "expected": " replaceReducer,", + "returned": null + }, + { + "line": 392, + "expected": " [$$observable]: observable", + "returned": null + }, + { + "line": 393, + "expected": " } as unknown as Store & Ext", + "returned": null + }, + { + "line": 394, + "expected": " return store", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + } + ], + "sourceBytes": 0, + "stdoutBytes": 0, + "members": [], + "literalWitnessFacts": 0, + "sourceEvidenceFacts": 0, + "explicitNativeFacts": 0 + }, + { + "repository": "redux", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "truncated-or-unknown", + "judgments": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 121, + "expected": " if (typeof enhancer !== 'undefined') {", + "returned": null + }, + { + "line": 122, + "expected": " if (typeof enhancer !== 'function') {", + "returned": null + }, + { + "line": 123, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 124, + "expected": " `Expected the enhancer to be a function. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 125, + "expected": " enhancer", + "returned": null + }, + { + "line": 126, + "expected": " )}'`", + "returned": null + }, + { + "line": 127, + "expected": " )", + "returned": null + }, + { + "line": 128, + "expected": " }", + "returned": null + }, + { + "line": 129, + "expected": "", + "returned": null + }, + { + "line": 130, + "expected": " return enhancer(createStore)(", + "returned": null + }, + { + "line": 131, + "expected": " reducer,", + "returned": null + }, + { + "line": 132, + "expected": " preloadedState as PreloadedState | undefined", + "returned": null + }, + { + "line": 133, + "expected": " )", + "returned": null + }, + { + "line": 134, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 293, + "expected": " if (isDispatching) {", + "returned": null + }, + { + "line": 294, + "expected": " throw new Error('Reducers may not dispatch actions.')", + "returned": null + }, + { + "line": 295, + "expected": " }", + "returned": null + }, + { + "line": 296, + "expected": "", + "returned": null + }, + { + "line": 297, + "expected": " try {", + "returned": null + }, + { + "line": 298, + "expected": " isDispatching = true", + "returned": null + }, + { + "line": 299, + "expected": " currentState = currentReducer(currentState, action)", + "returned": null + }, + { + "line": 300, + "expected": " } finally {", + "returned": null + }, + { + "line": 301, + "expected": " isDispatching = false", + "returned": null + }, + { + "line": 302, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 152, + "expected": " function ensureCanMutateNextListeners() {", + "returned": null + }, + { + "line": 153, + "expected": " if (nextListeners === currentListeners) {", + "returned": null + }, + { + "line": 154, + "expected": " nextListeners = new Map()", + "returned": null + }, + { + "line": 155, + "expected": " currentListeners.forEach((listener, key) => {", + "returned": null + }, + { + "line": 156, + "expected": " nextListeners.set(key, listener)", + "returned": null + }, + { + "line": 157, + "expected": " })", + "returned": null + }, + { + "line": 158, + "expected": " }", + "returned": null + }, + { + "line": 159, + "expected": " }", + "returned": null + }, + { + "line": 221, + "expected": " ensureCanMutateNextListeners()", + "returned": null + }, + { + "line": 222, + "expected": " const listenerId = listenerIdCounter++", + "returned": null + }, + { + "line": 223, + "expected": " nextListeners.set(listenerId, listener)", + "returned": null + }, + { + "line": 224, + "expected": "", + "returned": null + }, + { + "line": 225, + "expected": " return function unsubscribe() {", + "returned": null + }, + { + "line": 226, + "expected": " if (!isSubscribed) {", + "returned": null + }, + { + "line": 227, + "expected": " return", + "returned": null + }, + { + "line": 228, + "expected": " }", + "returned": null + }, + { + "line": 229, + "expected": "", + "returned": null + }, + { + "line": 230, + "expected": " if (isDispatching) {", + "returned": null + }, + { + "line": 231, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 232, + "expected": " 'You may not unsubscribe from a store listener while the reducer is executing. ' +", + "returned": null + }, + { + "line": 233, + "expected": " 'See https://redux.js.org/api/store#subscribelistener for more details.'", + "returned": null + }, + { + "line": 234, + "expected": " )", + "returned": null + }, + { + "line": 235, + "expected": " }", + "returned": null + }, + { + "line": 236, + "expected": "", + "returned": null + }, + { + "line": 237, + "expected": " isSubscribed = false", + "returned": null + }, + { + "line": 238, + "expected": "", + "returned": null + }, + { + "line": 239, + "expected": " ensureCanMutateNextListeners()", + "returned": null + }, + { + "line": 240, + "expected": " nextListeners.delete(listenerId)", + "returned": null + }, + { + "line": 241, + "expected": " currentListeners = null", + "returned": null + }, + { + "line": 242, + "expected": " }", + "returned": null + }, + { + "line": 243, + "expected": " }", + "returned": null + }, + { + "line": 304, + "expected": " const listeners = (currentListeners = nextListeners)", + "returned": null + }, + { + "line": 305, + "expected": " listeners.forEach(listener => {", + "returned": null + }, + { + "line": 306, + "expected": " listener()", + "returned": null + }, + { + "line": 307, + "expected": " })", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 344, + "expected": " function observable() {", + "returned": null + }, + { + "line": 345, + "expected": " const outerSubscribe = subscribe", + "returned": null + }, + { + "line": 346, + "expected": " return {", + "returned": null + }, + { + "line": 347, + "expected": " /**", + "returned": null + }, + { + "line": 348, + "expected": " * The minimal observable subscription method.", + "returned": null + }, + { + "line": 349, + "expected": " * @param observer Any object that can be used as an observer.", + "returned": null + }, + { + "line": 350, + "expected": " * The observer object should have a `next` method.", + "returned": null + }, + { + "line": 351, + "expected": " * @returns An object with an `unsubscribe` method that can", + "returned": null + }, + { + "line": 352, + "expected": " * be used to unsubscribe the observable from the store, and prevent further", + "returned": null + }, + { + "line": 353, + "expected": " * emission of values from the observable.", + "returned": null + }, + { + "line": 354, + "expected": " */", + "returned": null + }, + { + "line": 355, + "expected": " subscribe(observer: unknown) {", + "returned": null + }, + { + "line": 356, + "expected": " if (typeof observer !== 'object' || observer === null) {", + "returned": null + }, + { + "line": 357, + "expected": " throw new TypeError(", + "returned": null + }, + { + "line": 358, + "expected": " `Expected the observer to be an object. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 359, + "expected": " observer", + "returned": null + }, + { + "line": 360, + "expected": " )}'`", + "returned": null + }, + { + "line": 361, + "expected": " )", + "returned": null + }, + { + "line": 362, + "expected": " }", + "returned": null + }, + { + "line": 363, + "expected": "", + "returned": null + }, + { + "line": 364, + "expected": " function observeState() {", + "returned": null + }, + { + "line": 365, + "expected": " const observerAsObserver = observer as Observer", + "returned": null + }, + { + "line": 366, + "expected": " if (observerAsObserver.next) {", + "returned": null + }, + { + "line": 367, + "expected": " observerAsObserver.next(getState())", + "returned": null + }, + { + "line": 368, + "expected": " }", + "returned": null + }, + { + "line": 369, + "expected": " }", + "returned": null + }, + { + "line": 370, + "expected": "", + "returned": null + }, + { + "line": 371, + "expected": " observeState()", + "returned": null + }, + { + "line": 372, + "expected": " const unsubscribe = outerSubscribe(observeState)", + "returned": null + }, + { + "line": 373, + "expected": " return { unsubscribe }", + "returned": null + }, + { + "line": 374, + "expected": " },", + "returned": null + }, + { + "line": 375, + "expected": "", + "returned": null + }, + { + "line": 376, + "expected": " [$$observable]() {", + "returned": null + }, + { + "line": 377, + "expected": " return this", + "returned": null + }, + { + "line": 378, + "expected": " }", + "returned": null + }, + { + "line": 379, + "expected": " }", + "returned": null + }, + { + "line": 380, + "expected": " }", + "returned": null + }, + { + "line": 381, + "expected": "", + "returned": null + }, + { + "line": 382, + "expected": " // When a store is created, an \"INIT\" action is dispatched so that every", + "returned": null + }, + { + "line": 383, + "expected": " // reducer returns their initial state. This effectively populates", + "returned": null + }, + { + "line": 384, + "expected": " // the initial state tree.", + "returned": null + }, + { + "line": 385, + "expected": " dispatch({ type: ActionTypes.INIT } as A)", + "returned": null + }, + { + "line": 386, + "expected": "", + "returned": null + }, + { + "line": 387, + "expected": " const store = {", + "returned": null + }, + { + "line": 388, + "expected": " dispatch: dispatch as Dispatch,", + "returned": null + }, + { + "line": 389, + "expected": " subscribe,", + "returned": null + }, + { + "line": 390, + "expected": " getState,", + "returned": null + }, + { + "line": 391, + "expected": " replaceReducer,", + "returned": null + }, + { + "line": 392, + "expected": " [$$observable]: observable", + "returned": null + }, + { + "line": 393, + "expected": " } as unknown as Store & Ext", + "returned": null + }, + { + "line": 394, + "expected": " return store", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "No uniquely selected owner after bounded recall; retain all four facts in the denominator." + } + ], + "sourceBytes": 0, + "stdoutBytes": 0, + "literalWitnessFacts": 0, + "sourceEvidenceFacts": 0, + "explicitNativeFacts": 0 + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 121, + "expected": " if (typeof enhancer !== 'undefined') {", + "returned": null + }, + { + "line": 122, + "expected": " if (typeof enhancer !== 'function') {", + "returned": null + }, + { + "line": 123, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 124, + "expected": " `Expected the enhancer to be a function. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 125, + "expected": " enhancer", + "returned": null + }, + { + "line": 126, + "expected": " )}'`", + "returned": null + }, + { + "line": 127, + "expected": " )", + "returned": null + }, + { + "line": 128, + "expected": " }", + "returned": null + }, + { + "line": 129, + "expected": "", + "returned": null + }, + { + "line": 130, + "expected": " return enhancer(createStore)(", + "returned": null + }, + { + "line": 131, + "expected": " reducer,", + "returned": null + }, + { + "line": 132, + "expected": " preloadedState as PreloadedState | undefined", + "returned": null + }, + { + "line": 133, + "expected": " )", + "returned": null + }, + { + "line": 134, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "redux-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 365, + "expected": " const observerAsObserver = observer as Observer", + "returned": " const observerAsObserver = observer a" + }, + { + "line": 366, + "expected": " if (observerAsObserver.next) {", + "returned": null + }, + { + "line": 367, + "expected": " observerAsObserver.next(getState())", + "returned": null + }, + { + "line": 368, + "expected": " }", + "returned": null + }, + { + "line": 369, + "expected": " }", + "returned": null + }, + { + "line": 370, + "expected": "", + "returned": null + }, + { + "line": 371, + "expected": " observeState()", + "returned": null + }, + { + "line": 372, + "expected": " const unsubscribe = outerSubscribe(observeState)", + "returned": null + }, + { + "line": 373, + "expected": " return { unsubscribe }", + "returned": null + }, + { + "line": 374, + "expected": " },", + "returned": null + }, + { + "line": 375, + "expected": "", + "returned": null + }, + { + "line": 376, + "expected": " [$$observable]() {", + "returned": null + }, + { + "line": 377, + "expected": " return this", + "returned": null + }, + { + "line": 378, + "expected": " }", + "returned": null + }, + { + "line": 379, + "expected": " }", + "returned": null + }, + { + "line": 380, + "expected": " }", + "returned": null + }, + { + "line": 381, + "expected": "", + "returned": null + }, + { + "line": 382, + "expected": " // When a store is created, an \"INIT\" action is dispatched so that every", + "returned": null + }, + { + "line": 383, + "expected": " // reducer returns their initial state. This effectively populates", + "returned": null + }, + { + "line": 384, + "expected": " // the initial state tree.", + "returned": null + }, + { + "line": 385, + "expected": " dispatch({ type: ActionTypes.INIT } as A)", + "returned": null + }, + { + "line": 386, + "expected": "", + "returned": null + }, + { + "line": 387, + "expected": " const store = {", + "returned": null + }, + { + "line": 388, + "expected": " dispatch: dispatch as Dispatch,", + "returned": null + }, + { + "line": 389, + "expected": " subscribe,", + "returned": null + }, + { + "line": 390, + "expected": " getState,", + "returned": null + }, + { + "line": 391, + "expected": " replaceReducer,", + "returned": null + }, + { + "line": 392, + "expected": " [$$observable]: observable", + "returned": null + }, + { + "line": 393, + "expected": " } as unknown as Store & Ext", + "returned": null + }, + { + "line": 394, + "expected": " return store", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1785, + "neighborWireBytes": 1897, + "returnedMembershipRows": 2, + "windows": [ + { + "file": "src/createStore.ts", + "startLine": 152, + "lastReturnedLine": 269, + "sourceBytes": 4521, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 270, + "lastReturnedLine": 365, + "sourceBytes": 3479, + "partial": true + } + ], + "omittedGroups": 0, + "literalWitnessFacts": 2, + "sourceEvidenceFacts": 2, + "explicitNativeFacts": 0 + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "native-members", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 972, + "expected": "", + "returned": null + }, + { + "line": 976, + "expected": " for ancestor in self.stack_path.iter().rev() {", + "returned": " for ancestor in" + }, + { + "line": 977, + "expected": " let is_same = ancestor", + "returned": null + }, + { + "line": 978, + "expected": " .is_same(&hchild)", + "returned": null + }, + { + "line": 979, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 980, + "expected": " if is_same {", + "returned": null + }, + { + "line": 981, + "expected": " return Err(Error::from_loop(", + "returned": null + }, + { + "line": 982, + "expected": " self.depth,", + "returned": null + }, + { + "line": 983, + "expected": " &ancestor.path,", + "returned": null + }, + { + "line": 984, + "expected": " child.as_ref(),", + "returned": null + }, + { + "line": 985, + "expected": " ));", + "returned": null + }, + { + "line": 986, + "expected": " }", + "returned": null + }, + { + "line": 987, + "expected": " }", + "returned": null + }, + { + "line": 988, + "expected": " Ok(())", + "returned": null + }, + { + "line": 989, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 883, + "expected": "", + "returned": null + } + ], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Only blank separator lines between verified callable spans are missing; both complete implementation bodies establishing the fact are returned." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 13045, + "members": [ + { + "id": "sha256:5c1a2f2ab2725e97f0e936e554e3381bc72abac584341846089152518b8314b1", + "name": ".next()", + "file": "src/lib.rs", + "startLine": 687, + "lastReturnedLine": 734, + "sourceBytes": 1812, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c3fe8fc9e9d0038d0c1191e7cf0d4ee7783cebd55c755a07c627023b60a3d188", + "name": ".skip_current_dir()", + "file": "src/lib.rs", + "startLine": 781, + "lastReturnedLine": 785, + "sourceBytes": 117, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5704b7023e55533acf5164ddd3ae50467aea1abf4c19866f3e143ae7bf09d370", + "name": ".filter_entry()", + "file": "src/lib.rs", + "startLine": 833, + "lastReturnedLine": 838, + "sourceBytes": 169, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "name": ".handle_entry()", + "file": "src/lib.rs", + "startLine": 840, + "lastReturnedLine": 882, + "sourceBytes": 1726, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "name": ".get_deferred_dir()", + "file": "src/lib.rs", + "startLine": 884, + "lastReturnedLine": 899, + "sourceBytes": 607, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "name": ".push()", + "file": "src/lib.rs", + "startLine": 901, + "lastReturnedLine": 948, + "sourceBytes": 2459, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3d843599c82e0cf88d7512278cc2b4f2ffc87c5797785e0c9caad89ab4af8f98", + "name": ".pop()", + "file": "src/lib.rs", + "startLine": 950, + "lastReturnedLine": 959, + "sourceBytes": 484, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "name": ".follow()", + "file": "src/lib.rs", + "startLine": 961, + "lastReturnedLine": 971, + "sourceBytes": 431, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "name": ".check_loop()", + "file": "src/lib.rs", + "startLine": 973, + "lastReturnedLine": 976, + "sourceBytes": 195, + "truncated": true, + "digestVerified": true + } + ], + "omittedMembers": 2, + "truncated": true, + "literalWitnessFacts": 1, + "sourceEvidenceFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 901, + "expected": " fn push(&mut self, dent: &DirEntry) -> Result<()> {", + "returned": null + }, + { + "line": 902, + "expected": " // Make room for another open file descriptor if we've hit the max.", + "returned": null + }, + { + "line": 903, + "expected": " let free =", + "returned": null + }, + { + "line": 904, + "expected": " self.stack_list.len().checked_sub(self.oldest_opened).unwrap();", + "returned": null + }, + { + "line": 905, + "expected": " if free == self.opts.max_open {", + "returned": null + }, + { + "line": 906, + "expected": " self.stack_list[self.oldest_opened].close();", + "returned": null + }, + { + "line": 907, + "expected": " }", + "returned": null + }, + { + "line": 908, + "expected": " // Open a handle to reading the directory's entries.", + "returned": null + }, + { + "line": 909, + "expected": " let rd = fs::read_dir(dent.path()).map_err(|err| {", + "returned": null + }, + { + "line": 910, + "expected": " Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))", + "returned": null + }, + { + "line": 911, + "expected": " });", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 961, + "expected": " fn follow(&self, mut dent: DirEntry) -> Result {", + "returned": null + }, + { + "line": 962, + "expected": " dent =", + "returned": null + }, + { + "line": 963, + "expected": " DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;", + "returned": null + }, + { + "line": 964, + "expected": " // The only way a symlink can cause a loop is if it points", + "returned": null + }, + { + "line": 965, + "expected": " // to a directory. Otherwise, it always points to a leaf", + "returned": null + }, + { + "line": 966, + "expected": " // and we can omit any loop checks.", + "returned": null + }, + { + "line": 967, + "expected": " if dent.is_dir() {", + "returned": null + }, + { + "line": 968, + "expected": " self.check_loop(dent.path())?;", + "returned": null + }, + { + "line": 969, + "expected": " }", + "returned": null + }, + { + "line": 970, + "expected": " Ok(dent)", + "returned": null + }, + { + "line": 971, + "expected": " }", + "returned": null + }, + { + "line": 972, + "expected": "", + "returned": null + }, + { + "line": 973, + "expected": " fn check_loop>(&self, child: P) -> Result<()> {", + "returned": null + }, + { + "line": 974, + "expected": " let hchild = Handle::from_path(&child)", + "returned": null + }, + { + "line": 975, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 976, + "expected": " for ancestor in self.stack_path.iter().rev() {", + "returned": null + }, + { + "line": 977, + "expected": " let is_same = ancestor", + "returned": null + }, + { + "line": 978, + "expected": " .is_same(&hchild)", + "returned": null + }, + { + "line": 979, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 980, + "expected": " if is_same {", + "returned": null + }, + { + "line": 981, + "expected": " return Err(Error::from_loop(", + "returned": null + }, + { + "line": 982, + "expected": " self.depth,", + "returned": null + }, + { + "line": 983, + "expected": " &ancestor.path,", + "returned": null + }, + { + "line": 984, + "expected": " child.as_ref(),", + "returned": null + }, + { + "line": 985, + "expected": " ));", + "returned": null + }, + { + "line": 986, + "expected": " }", + "returned": null + }, + { + "line": 987, + "expected": " }", + "returned": null + }, + { + "line": 988, + "expected": " Ok(())", + "returned": null + }, + { + "line": 989, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 873, + "expected": " }", + "returned": null + }, + { + "line": 874, + "expected": " if is_normal_dir && self.opts.contents_first {", + "returned": null + }, + { + "line": 875, + "expected": " self.deferred_dirs.push(dent);", + "returned": null + }, + { + "line": 876, + "expected": " None", + "returned": null + }, + { + "line": 877, + "expected": " } else if self.skippable() {", + "returned": null + }, + { + "line": 878, + "expected": " None", + "returned": null + }, + { + "line": 879, + "expected": " } else {", + "returned": null + }, + { + "line": 880, + "expected": " Some(Ok(dent))", + "returned": null + }, + { + "line": 881, + "expected": " }", + "returned": null + }, + { + "line": 882, + "expected": " }", + "returned": null + }, + { + "line": 883, + "expected": "", + "returned": null + }, + { + "line": 884, + "expected": " fn get_deferred_dir(&mut self) -> Option {", + "returned": null + }, + { + "line": 885, + "expected": " if self.opts.contents_first {", + "returned": null + }, + { + "line": 886, + "expected": " if self.depth < self.deferred_dirs.len() {", + "returned": null + }, + { + "line": 887, + "expected": " // Unwrap is safe here because we've guaranteed that", + "returned": null + }, + { + "line": 888, + "expected": " // `self.deferred_dirs.len()` can never be less than 1", + "returned": null + }, + { + "line": 889, + "expected": " let deferred: DirEntry = self", + "returned": null + }, + { + "line": 890, + "expected": " .deferred_dirs", + "returned": null + }, + { + "line": 891, + "expected": " .pop()", + "returned": null + }, + { + "line": 892, + "expected": " .expect(\"BUG: deferred_dirs should be non-empty\");", + "returned": null + }, + { + "line": 893, + "expected": " if !self.skippable() {", + "returned": null + }, + { + "line": 894, + "expected": " return Some(deferred);", + "returned": null + }, + { + "line": 895, + "expected": " }", + "returned": null + }, + { + "line": 896, + "expected": " }", + "returned": null + }, + { + "line": 897, + "expected": " }", + "returned": null + }, + { + "line": 898, + "expected": " None", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 4237, + "neighborWireBytes": 44444, + "returnedMembershipRows": 20, + "windows": [ + { + "file": "src/lib.rs", + "startLine": 568, + "lastReturnedLine": 572, + "sourceBytes": 167, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 573, + "lastReturnedLine": 578, + "sourceBytes": 348, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 579, + "lastReturnedLine": 585, + "sourceBytes": 253, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 586, + "lastReturnedLine": 590, + "sourceBytes": 283, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 591, + "lastReturnedLine": 593, + "sourceBytes": 135, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 594, + "lastReturnedLine": 597, + "sourceBytes": 207, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 598, + "lastReturnedLine": 604, + "sourceBytes": 318, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 605, + "lastReturnedLine": 679, + "sourceBytes": 2788, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 680, + "lastReturnedLine": 686, + "sourceBytes": 273, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 687, + "lastReturnedLine": 776, + "sourceBytes": 3228, + "partial": true + } + ], + "omittedGroups": 10, + "literalWitnessFacts": 1, + "sourceEvidenceFacts": 1, + "explicitNativeFacts": 0 + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 902, + "expected": " // Make room for another open file descriptor if we've hit the max.", + "returned": " // Make room for another open file descriptor if we" + }, + { + "line": 903, + "expected": " let free =", + "returned": null + }, + { + "line": 904, + "expected": " self.stack_list.len().checked_sub(self.oldest_opened).unwrap();", + "returned": null + }, + { + "line": 905, + "expected": " if free == self.opts.max_open {", + "returned": null + }, + { + "line": 906, + "expected": " self.stack_list[self.oldest_opened].close();", + "returned": null + }, + { + "line": 907, + "expected": " }", + "returned": null + }, + { + "line": 908, + "expected": " // Open a handle to reading the directory's entries.", + "returned": null + }, + { + "line": 909, + "expected": " let rd = fs::read_dir(dent.path()).map_err(|err| {", + "returned": null + }, + { + "line": 910, + "expected": " Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))", + "returned": null + }, + { + "line": 911, + "expected": " });", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 961, + "expected": " fn follow(&self, mut dent: DirEntry) -> Result {", + "returned": null + }, + { + "line": 962, + "expected": " dent =", + "returned": null + }, + { + "line": 963, + "expected": " DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;", + "returned": null + }, + { + "line": 964, + "expected": " // The only way a symlink can cause a loop is if it points", + "returned": null + }, + { + "line": 965, + "expected": " // to a directory. Otherwise, it always points to a leaf", + "returned": null + }, + { + "line": 966, + "expected": " // and we can omit any loop checks.", + "returned": null + }, + { + "line": 967, + "expected": " if dent.is_dir() {", + "returned": null + }, + { + "line": 968, + "expected": " self.check_loop(dent.path())?;", + "returned": null + }, + { + "line": 969, + "expected": " }", + "returned": null + }, + { + "line": 970, + "expected": " Ok(dent)", + "returned": null + }, + { + "line": 971, + "expected": " }", + "returned": null + }, + { + "line": 972, + "expected": "", + "returned": null + }, + { + "line": 973, + "expected": " fn check_loop>(&self, child: P) -> Result<()> {", + "returned": null + }, + { + "line": 974, + "expected": " let hchild = Handle::from_path(&child)", + "returned": null + }, + { + "line": 975, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 976, + "expected": " for ancestor in self.stack_path.iter().rev() {", + "returned": null + }, + { + "line": 977, + "expected": " let is_same = ancestor", + "returned": null + }, + { + "line": 978, + "expected": " .is_same(&hchild)", + "returned": null + }, + { + "line": 979, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 980, + "expected": " if is_same {", + "returned": null + }, + { + "line": 981, + "expected": " return Err(Error::from_loop(", + "returned": null + }, + { + "line": 982, + "expected": " self.depth,", + "returned": null + }, + { + "line": 983, + "expected": " &ancestor.path,", + "returned": null + }, + { + "line": 984, + "expected": " child.as_ref(),", + "returned": null + }, + { + "line": 985, + "expected": " ));", + "returned": null + }, + { + "line": 986, + "expected": " }", + "returned": null + }, + { + "line": 987, + "expected": " }", + "returned": null + }, + { + "line": 988, + "expected": " Ok(())", + "returned": null + }, + { + "line": 989, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "All required source lines are returned; differences from literal witnesses, if any, are only leading indentation at recorded span starts." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1336, + "neighborWireBytes": 1448, + "returnedMembershipRows": 11, + "windows": [ + { + "file": "src/lib.rs", + "startLine": 687, + "lastReturnedLine": 780, + "sourceBytes": 3391, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 781, + "lastReturnedLine": 832, + "sourceBytes": 1974, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 833, + "lastReturnedLine": 839, + "sourceBytes": 175, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 840, + "lastReturnedLine": 883, + "sourceBytes": 1732, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 884, + "lastReturnedLine": 900, + "sourceBytes": 613, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 901, + "lastReturnedLine": 902, + "sourceBytes": 115, + "partial": true + } + ], + "omittedGroups": 5, + "literalWitnessFacts": 2, + "sourceEvidenceFacts": 2, + "explicitNativeFacts": 0 + } + ], + "adjudication": { + "reviewer": "Same agent, separate verification script and post-capture semantic review; not independent human adjudication.", + "literalPolicy": "Exact complete witness line text and line locations. Leading indentation differences and omitted blank separators make literal coverage fail; this is a strict formatting/coverage measure, not a semantic quality score.", + "semanticPolicy": "Complete implementation fact only. Leading indentation and blank separator differences do not remove code meaning. The class header may be supplied by the independently verified exact owner identity, but decorator semantics require the decorator in the evidence. No credit for absent code, partial statements or guessed property/ownership behavior.", + "nativeClaims": "The new mode renders node, relationship and source evidence. It authors zero task-specific responsibility explanations.", + "anchorReview": "All 130 returned membership anchors were inspected against source declarations (69 Compass, 61 Graphify). Graphify sites match displayed member declarations in this sample; this does not establish general relation-site identity safety, full relationship precision, or complete membership recall.", + "nativeVerification": "54 displayed member IDs match the independently recomputed directed containment traversal and source order. All 54 complete stored span digests match pinned bytes; rendered prefixes match their shared 8000-byte quota. Nested jsoup methods are reached through type containers. No unavailable members in this sample.", + "controlVerification": "Nine recorded get_neighbors calls, with unchanged root IDs and no filters or pagination. Recompute all windows from captured returned anchors only. No printed Graphify truncation marker was observed; this does not prove complete graph membership. Compass reports no neighbor or transport truncation. Omitted source-window groups remain explicit." + }, + "comparison": { + "previousNativeDeclarationEvidence": { + "compass": 7, + "graphify": 0, + "facts": 20 + }, + "previousQueryPlusSourceRead": { + "compass": 11, + "graphify": 13, + "facts": 20 + }, + "nativeDelta": "Seven gained facts and one lost fact: Chi +3, jsoup +1, WalkDir +3, Click -1; Redux unchanged unresolved. No equivalent Graphify member-mode operation is asserted.", + "controlDifference": "Graphify gains one jsoup fact, two Redux facts and one WalkDir fact over Compass under this policy. Returned field anchors consume Compass window budget; Graphify exposes fewer such anchors. Both follow the same source-window algorithm.", + "noAggregation": "Keep all three workflows and the declaration baseline separate. No best-of-arms score, latency, equal-I/O efficiency, synthesized-explanation or god-object-quality claim." + }, + "validation": { + "baseCommit": "bac936f9eae3890e23f0776a692c145409134550", + "sourceSha256": { + "crates/compass-query/src/explanation_members.rs": "c6b9581d44012f8d4218c2d79408f2527702e56ce0ea0d6c967922c5daff2081", + "crates/compass-query/src/neighbors.rs": "20988f2ac56bd6e077a4bcbc317fc7a21f20e9ae705afcd642def1e507c271c9", + "crates/compass-query/src/traversal.rs": "d7d581ecfa1a32bbd76c3be6e2ba1dd4db1bcf3b1a316263b50c93a4052def16", + "crates/compass-query/src/lib.rs": "6009f8ba46df2aeefc6a01a92ae749a2bf6e7b06fb4022136c9e84f6c83d2b65", + "crates/compass-query/tests/explanation_members.rs": "e5bd2a42b81314a847212dc5af877700adf0d9b3d79f804cbca1e2e109e6c0fb", + "crates/compass-query/tests/explanation_source.rs": "34186c0a032ee576f587d1302d78f07959270ab9e139737414529b7781c869b6", + "crates/compass-cli/src/lib.rs": "057e9325c6cc85fe5da0f0f8dfafecbcd2e85e257f6d9355af15462308e7a002", + "crates/compass-cli/src/help.rs": "1887ad3a201c7bf3c35eb18677f26f8d6a573948ce90f41d81234856978e3f57", + "crates/compass-cli/tests/code_query_cli.rs": "72b9ede4e56262328636c08e7f4666e7f859d592edd71377c3cc45f0b7005c63" + }, + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 3.71 + }, + { + "name": "query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--test", + "explanation_source", + "--test", + "explanation_members", + "--locked" + ], + "exitCode": 0, + "seconds": 5.09 + }, + { + "name": "cli-query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "code_query_cli", + "--locked" + ], + "exitCode": 0, + "seconds": 59.87 + }, + { + "name": "mcp-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--locked" + ], + "exitCode": 0, + "seconds": 38.96 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 14.21 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 90.29 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 9.2 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.08 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "--locked", + "-p", + "compass-cli", + "--bin", + "compass" + ], + "exitCode": 0, + "seconds": 1.87 + } + ] + }, + "validationScope": { + "passed": "fmt; 13 focused query tests; 40 CLI query tests; 60 MCP tests; workspace Clippy; 1106 workspace tests, 2 ignored; 9 product tests; product boundary; CLI build.", + "notRun": "JS/viewer and extraction/resolution publication qualification gates were not rerun because only source retrieval, CLI rendering and documentation changed. The general benchmark harness was not modified.", + "binaryBinding": "Final CLI build bytes match the frozen evaluated binary; every recorded Rust source hash matches productCommit." + }, + "attempts": [ + "Initial collector failed before any public requests because its hash helper passed an unsupported read bound. Preserve capture.log, collect-attempt-01.py; corrected streaming hashing and used fresh capture-02 directory.", + "First verifier split the MEMBER SOURCES heading as a member. Preserve verification-01.log and verify-capture-attempt-01.py. Corrected parsing; verification-02 passed. Final verifier also checks source byte-to-line consistency and passes." + ], + "artifacts": { + "member-source-02/build-final.log": "9c112f5e179b81b74ab3c14ed843179185008da9475637274660aed074d421a5", + "member-source-02/capture-02/capture.json": "96a88b74d425287525311c647b97ef7daa0c659ada56c31645be83def3e3c4a5", + "member-source-02/capture-02/collect.py": "7a95e6dddb8a8b69ea21082163d5125783816f5ebb460dfcf3f691f9a608da69", + "member-source-02/capture-02/community_identity.py": "5bc3d1c574211aa3180a2334fe6c758043f384651948b7077aba3ea879de4503", + "member-source-02/capture-02/community_navigation.py": "808df1c7b15c8c2afc86ac1ed09dc4f339c98a77d7a7b14eb4866e3da1c8c0f8", + "member-source-02/capture-02/community_tasks.py": "77f599ff7ee7eebfe4b590fee6c771ecfb3ed475d1944e37bcabc110634d3660", + "member-source-02/capture-02/graphify-mcp-environment.json": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535", + "member-source-02/capture-02/mcp_compare.py": "d23f093a1300fcf825457f917694097f0f8b8dc8ee8d671946032f5e4e89a5dc", + "member-source-02/capture-02/mcp_transport.py": "daaad69c554f824a1df94f0ef93cb9d4c6e9b4f4b712d1611323d524e10d44ee", + "member-source-02/capture-02/member_source_development_registration.json": "e95a8b9964a9b5482d66051bc533edb57f93ede59a0a576fb95a85f0460f3f76", + "member-source-02/capture-02/raw/chi/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/chi/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "member-source-02/capture-02/raw/chi/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/chi/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/chi/compass/mcp/03.response.jsonl": "64d26fe612b4c19a3c88ba6d5548476cf5322cf118de8d206b2698ce99b02a65", + "member-source-02/capture-02/raw/chi/compass/mcp/04.request.json": "fa0a888eb5b3c0af33258fcda7dd5fda8d8524b58106a73dd14a5a5ce751a653", + "member-source-02/capture-02/raw/chi/compass/mcp/04.response.jsonl": "bee27b222019288810fb48facfe2f2ce43153b8e3f1d88f3cb6bc37bb628e1b5", + "member-source-02/capture-02/raw/chi/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/chi/compass/members.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/chi/compass/members.stdout": "a827aea6e1c28e363d972da58943349455dd43154d162d5adecfffaaf2bcd41f", + "member-source-02/capture-02/raw/chi/compass/windows/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "member-source-02/capture-02/raw/chi/compass/windows/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "member-source-02/capture-02/raw/chi/compass/windows/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "member-source-02/capture-02/raw/chi/compass/windows/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "member-source-02/capture-02/raw/chi/compass/windows/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "member-source-02/capture-02/raw/chi/compass/windows/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "member-source-02/capture-02/raw/chi/compass/windows/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "member-source-02/capture-02/raw/chi/compass/windows/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "member-source-02/capture-02/raw/chi/compass/windows/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "member-source-02/capture-02/raw/chi/compass/windows/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "member-source-02/capture-02/raw/chi/compass/windows/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "member-source-02/capture-02/raw/chi/compass/windows/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "member-source-02/capture-02/raw/chi/compass/windows/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "member-source-02/capture-02/raw/chi/compass/windows/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "member-source-02/capture-02/raw/chi/compass/windows/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "member-source-02/capture-02/raw/chi/compass/windows/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "member-source-02/capture-02/raw/chi/compass/windows/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "member-source-02/capture-02/raw/chi/compass/windows/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "member-source-02/capture-02/raw/chi/compass/windows/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "member-source-02/capture-02/raw/chi/compass/windows/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "member-source-02/capture-02/raw/chi/compass/windows/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "member-source-02/capture-02/raw/chi/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/chi/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "member-source-02/capture-02/raw/chi/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/chi/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/chi/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "member-source-02/capture-02/raw/chi/graphify/mcp/04.request.json": "099de0bb0962487971342d734b860bc412719fd1469fddc833ba46ec7c7fee98", + "member-source-02/capture-02/raw/chi/graphify/mcp/04.response.jsonl": "07cf8f5c1e427dedccf1f84b1388c0969e74a1f2c08c7b628ee7443f36dc7d0c", + "member-source-02/capture-02/raw/chi/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/chi/graphify/windows/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "member-source-02/capture-02/raw/chi/graphify/windows/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "member-source-02/capture-02/raw/chi/graphify/windows/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "member-source-02/capture-02/raw/chi/graphify/windows/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "member-source-02/capture-02/raw/chi/graphify/windows/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "member-source-02/capture-02/raw/chi/graphify/windows/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "member-source-02/capture-02/raw/chi/graphify/windows/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "member-source-02/capture-02/raw/chi/graphify/windows/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "member-source-02/capture-02/raw/chi/graphify/windows/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "member-source-02/capture-02/raw/chi/graphify/windows/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "member-source-02/capture-02/raw/chi/graphify/windows/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "member-source-02/capture-02/raw/chi/graphify/windows/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "member-source-02/capture-02/raw/chi/graphify/windows/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "member-source-02/capture-02/raw/chi/graphify/windows/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "member-source-02/capture-02/raw/chi/graphify/windows/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "member-source-02/capture-02/raw/chi/graphify/windows/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "member-source-02/capture-02/raw/chi/graphify/windows/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "member-source-02/capture-02/raw/chi/graphify/windows/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "member-source-02/capture-02/raw/chi/graphify/windows/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "member-source-02/capture-02/raw/chi/graphify/windows/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "member-source-02/capture-02/raw/chi/graphify/windows/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "member-source-02/capture-02/raw/click/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/click/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "member-source-02/capture-02/raw/click/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/click/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/click/compass/mcp/03.response.jsonl": "64d26fe612b4c19a3c88ba6d5548476cf5322cf118de8d206b2698ce99b02a65", + "member-source-02/capture-02/raw/click/compass/mcp/04.request.json": "fb8d2bc88ad1880a6ce22d99a63be7cd8d08ab0871a9f8622424a0304542a4aa", + "member-source-02/capture-02/raw/click/compass/mcp/04.response.jsonl": "a59049551958eb6a2914b0f4987029b33770f8cf4e5a688b67863c6beba8835d", + "member-source-02/capture-02/raw/click/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/click/compass/members.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/click/compass/members.stdout": "c12fa7fd7a03bcdf46578c982de39a5d539d41846e69dd3a93bd5a8b59bfcba2", + "member-source-02/capture-02/raw/click/compass/windows/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "member-source-02/capture-02/raw/click/compass/windows/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "member-source-02/capture-02/raw/click/compass/windows/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "member-source-02/capture-02/raw/click/compass/windows/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "member-source-02/capture-02/raw/click/compass/windows/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "member-source-02/capture-02/raw/click/compass/windows/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "member-source-02/capture-02/raw/click/compass/windows/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "member-source-02/capture-02/raw/click/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/click/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "member-source-02/capture-02/raw/click/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/click/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/click/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "member-source-02/capture-02/raw/click/graphify/mcp/04.request.json": "45d70070999af8bb5dac2cc3539f70fe83e41d5c898f53cd1fac42b55187988a", + "member-source-02/capture-02/raw/click/graphify/mcp/04.response.jsonl": "2e2b8b5d78de91e357e8a741b3959ee11bb1e48cfcbc71de6dd101570561c5fa", + "member-source-02/capture-02/raw/click/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/click/graphify/windows/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "member-source-02/capture-02/raw/click/graphify/windows/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "member-source-02/capture-02/raw/click/graphify/windows/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "member-source-02/capture-02/raw/click/graphify/windows/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "member-source-02/capture-02/raw/click/graphify/windows/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "member-source-02/capture-02/raw/click/graphify/windows/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "member-source-02/capture-02/raw/click/graphify/windows/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "member-source-02/capture-02/raw/jsoup/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/jsoup/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "member-source-02/capture-02/raw/jsoup/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/jsoup/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/jsoup/compass/mcp/03.response.jsonl": "64d26fe612b4c19a3c88ba6d5548476cf5322cf118de8d206b2698ce99b02a65", + "member-source-02/capture-02/raw/jsoup/compass/mcp/04.request.json": "a1e85cbdc8e510db04097cad55c25814a93c38b7b1f0372227e93481236d429b", + "member-source-02/capture-02/raw/jsoup/compass/mcp/04.response.jsonl": "baa465652b18341c09d564600481e0110f0e1e63079c5522540f83b90e7a5d4a", + "member-source-02/capture-02/raw/jsoup/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/jsoup/compass/members.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/jsoup/compass/members.stdout": "c4ef7ef86f96a867ab3b4bdafab8826ae1c940a34a9872fff0068dab32d73ed1", + "member-source-02/capture-02/raw/jsoup/compass/windows/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "member-source-02/capture-02/raw/jsoup/compass/windows/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "member-source-02/capture-02/raw/jsoup/compass/windows/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "member-source-02/capture-02/raw/jsoup/compass/windows/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "member-source-02/capture-02/raw/jsoup/compass/windows/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "member-source-02/capture-02/raw/jsoup/compass/windows/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "member-source-02/capture-02/raw/jsoup/compass/windows/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "member-source-02/capture-02/raw/jsoup/compass/windows/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/04.request.json": "a5d8506f70b3490e64d4cc9ed5e594dc9c3142343ab0ea47ef2f627e63c4ac51", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/04.response.jsonl": "de51e58563a7882c32c0729aa8574535fd5f4c2464ab1fcacb4f0a548aca5c51", + "member-source-02/capture-02/raw/jsoup/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/jsoup/graphify/windows/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "member-source-02/capture-02/raw/jsoup/graphify/windows/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "member-source-02/capture-02/raw/jsoup/graphify/windows/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "member-source-02/capture-02/raw/jsoup/graphify/windows/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "member-source-02/capture-02/raw/jsoup/graphify/windows/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "member-source-02/capture-02/raw/jsoup/graphify/windows/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "member-source-02/capture-02/raw/jsoup/graphify/windows/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "member-source-02/capture-02/raw/redux/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/redux/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "member-source-02/capture-02/raw/redux/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/redux/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/redux/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "member-source-02/capture-02/raw/redux/graphify/mcp/04.request.json": "e68cdb047d4c46a669be395ce3f665e42bc05be7aff7eaab19a3834b6ff51df1", + "member-source-02/capture-02/raw/redux/graphify/mcp/04.response.jsonl": "7fc2c10f6ad01773cef5d5d0a8f420a6fd05da5405af40e6dea393245f165dbc", + "member-source-02/capture-02/raw/redux/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/redux/graphify/windows/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "member-source-02/capture-02/raw/redux/graphify/windows/001.source": "104842ff0af72b77928d0a849bc5aa5e5bf080e95aeca8f8bc0baacd3dd90aa1", + "member-source-02/capture-02/raw/walkdir/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/walkdir/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "member-source-02/capture-02/raw/walkdir/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/walkdir/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/walkdir/compass/mcp/03.response.jsonl": "64d26fe612b4c19a3c88ba6d5548476cf5322cf118de8d206b2698ce99b02a65", + "member-source-02/capture-02/raw/walkdir/compass/mcp/04.request.json": "88007595c05c740812b9033f2ee95b791edf9b2707d4f7ade7b768f046d1d612", + "member-source-02/capture-02/raw/walkdir/compass/mcp/04.response.jsonl": "894892461013ff8ea0bf43d1fdd32cc5fb97e101ded12eaf24dd45200fa98d97", + "member-source-02/capture-02/raw/walkdir/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/walkdir/compass/members.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/walkdir/compass/members.stdout": "a29d756672a534873059500754d482cc21f60c69b525a206e8a42e65f1176e62", + "member-source-02/capture-02/raw/walkdir/compass/windows/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "member-source-02/capture-02/raw/walkdir/compass/windows/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "member-source-02/capture-02/raw/walkdir/compass/windows/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "member-source-02/capture-02/raw/walkdir/compass/windows/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "member-source-02/capture-02/raw/walkdir/compass/windows/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "member-source-02/capture-02/raw/walkdir/compass/windows/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "member-source-02/capture-02/raw/walkdir/compass/windows/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "member-source-02/capture-02/raw/walkdir/compass/windows/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "member-source-02/capture-02/raw/walkdir/compass/windows/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "member-source-02/capture-02/raw/walkdir/compass/windows/009.source": "907ef6bd190a9985da364ec084579cacae6b06067d5a3bdfa8d98a2c3256eb9b", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/04.request.json": "24caf98a41faa25bd5103d2d458f36b9be08bc70a408ee37a1f35594325ff78a", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/04.response.jsonl": "dd345c5346476f0054a0c8ba1f4b4fcc552a63f79701d2f9de1328bb9ef44650", + "member-source-02/capture-02/raw/walkdir/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/capture-02/raw/walkdir/graphify/windows/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "member-source-02/capture-02/raw/walkdir/graphify/windows/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "member-source-02/capture-02/raw/walkdir/graphify/windows/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "member-source-02/capture-02/raw/walkdir/graphify/windows/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "member-source-02/capture-02/raw/walkdir/graphify/windows/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "member-source-02/capture-02/raw/walkdir/graphify/windows/005.source": "b0c0b5e23f7d4cf98d355ff7f08101ea772f51b09b278f51ab7bb66ad2aaf4a6", + "member-source-02/capture-02/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "member-source-02/capture-02.log": "4262aa9f199672649ef280bca6511a6e59b9f3651611d0f80c4ae25c422a9a54", + "member-source-02/capture.log": "2950ab87049f66e57a977f54b24c4c7bf960b48d8055022a4635885f565d135e", + "member-source-02/cli-query-tests-final.log": "ed725935bcfd27883a6b77d6f9e318f19e2a7f4e893e026fd78c90541f13e503", + "member-source-02/clippy-final.log": "55d20891684ab855b3f0830e781ad502f9b9c47a3e1ac044016769a262f6f787", + "member-source-02/collect-attempt-01.py": "e6178c5ccb4da9e81d555864fe86cc45df41840cc9a9d771d00729ce7a6d20c2", + "member-source-02/collect.py": "7a95e6dddb8a8b69ea21082163d5125783816f5ebb460dfcf3f691f9a608da69", + "member-source-02/fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/mcp-tests-final.log": "daad87200738aca3825b27790a5fddf6f163844b3748fb116e06ba7819dfb4a9", + "member-source-02/product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-source-02/product-tests-final.log": "40548fd0bd61eafef2d25ca799ca5ac271b0ea033ff79bbc4553e451622ab30b", + "member-source-02/query-tests-final.log": "04df29746e74c5916a094b6635bae03d1be38679ddf0af035e6da8dc46b00f86", + "member-source-02/validate.py": "a13b0ba21ca5e370e78249348902480215907f28b691176cd5f63db91e393ca9", + "member-source-02/validation.json": "5e5fdb61fa4d1b802055f7a9dc2ecde0698a351e1232b59530430f0ade6f5cab", + "member-source-02/verification-01.log": "675e592251fe1b4f9261ae61cdf120a3b0026564eed6ed21d6b860d1c7667ce0", + "member-source-02/verification-02.log": "a06d655d6c58ea073baa191515e8651598736eb02ba8e29363603beec0d8501c", + "member-source-02/verification-final.log": "a06d655d6c58ea073baa191515e8651598736eb02ba8e29363603beec0d8501c", + "member-source-02/verified-summary.json": "a75c2d706cece1fc4ba02886ebebd243b30fd0c0d4195bbbbd4e1a70a364e522", + "member-source-02/verify-capture-attempt-01.py": "d4faee604c4efcf677a20f0fb0f5aac51f8d1145f3b7198510046515d796d97c", + "member-source-02/verify-capture-attempt-02.py": "3b1a1530806b803c2f568954d0a8b191b4ab7a40eb209aa34c9f9678d837c5cd", + "member-source-02/verify_capture.py": "570a2513f5b4941b3baf0d6a466971c272eecf45d1bee44f982ea5429c275bc1", + "member-source-02/workspace-tests-final.log": "06e06019ba7db6dbb909788ad6597ac8f9394708c964f25276c82efbe288edb9", + "member-source-02/write_review.py": "c4451376ec967f7589561fd12f60d543c2f6d90bc552a089c2ecf29d9a37f4ba" + }, + "limitations": [ + "Known purposive development panel, one subject per language, no representative precision estimate.", + "Whole-file verification and MCP/CLI metadata bytes differ; a shared source payload budget is not equal compute or disk I/O.", + "Member mode can omit annotations/decorators outside stored callable spans, even when source truncation is false.", + "Compass Redux candidate recall remains unresolved at the registered bound of 256.", + "Chi routeHTTP and WalkDir check_loop remain omitted or incomplete under native source budget.", + "Actual responsibility synthesis, god-object defect diagnosis, full caller/callee precision, longer walks, community task quality, and fresh confirmation remain outstanding." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index ec22c41f9..a4d050fd9 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2521,6 +2521,101 @@ under `native-explanation-01` and `native-explanation-02`. God-object diagnosis, responsibility synthesis, broader source precision, longer walks and fresh held-out confirmation remain outstanding. +## Recorded member source and a shared neighbor/source-window control + +Registration `bac936f9` freezes the existing five subjects, 20 responsibility +facts, graph snapshots and ten public endpoint-resolver responses. This is +known development evidence. No selector substitutions rescue Compass's Redux +candidate-limit failure. The opt-in `explain --source-members` implementation +(`6d4df994`) follows recorded outgoing containment through nested types and +returns callable spans in source order under one 8,000-byte source budget. +Default declaration excerpts are unchanged. This new Compass capability has +no claimed equivalent Graphify flag; its feature delta is not a paired win. + +A separate control gives each tool one public `get_neighbors` call with the +previously selected ID. It groups outgoing `contains` / `method` rows by their +returned file/start-line anchors, sorts them, and reads up to the next returned +anchor in that file (at most 4,096 bytes for its final anchor). All windows share +8,000 source bytes per subject. There are no extra pages, retries, fact-guided +selection or end-line advantages. Graphify's displayed relation sites are +checked against source declarations after capture. Windows do not establish +unique target identity. This is one reproducible agent policy, not an optimal +retrieval strategy or an equal-I/O experiment. + +| Subject | Compass member mode | Compass shared control | Graphify shared control | +| --- | ---: | ---: | ---: | +| Chi / Go | 3/4 | 3/4 | 3/4 | +| Click / Python | 3/4 | 4/4 | 4/4 | +| jsoup / Java | 4/4 | 3/4 | 4/4 | +| Redux / TypeScript | 0/4 | 0/4 | 2/4 | +| WalkDir / Rust | 3/4 | 1/4 | 2/4 | +| **Complete facts supported by source** | **13/20** | **11/20** | **15/20** | + +The earlier declaration-only native result remains Compass 7/20, Graphify +0/20; the earlier query-plus-source-read result remains Compass 11/20, +Graphify 13/20. Keep these workflows separate. None of these tools' native +renderings authors the requested mechanism explanations: explicit native +responsibility assertions remain zero. Source evidence is not a synthesized +answer or a god-object defect judgment. + +Member mode gains seven facts and loses one against declaration excerpts. +Separately defined Go and Rust methods become available, and the Java attribute +write now fits. However, Click's recorded getter span excludes `@property`. +The initializer and getter body alone cannot establish property semantics, so +that entire fact is denied. The shared windows retain the decorator and earn +that fact for both tools. Chi's route handler remains outside the native budget; +WalkDir's loop-check body is partial. Redux remains unresolved for Compass. + +Graphify leads the shared control by four facts: one Java, two TypeScript and +one Rust. Compass's returned Java field and Rust field/type anchors consume +window budget before later methods; Graphify exposes fewer such anchors. The +same policy therefore produces different coverage. No missing or partial code +is credited to close the gap. + +Exact literal witness coverage is 4/20 for member mode, 10/20 for Compass's +control and 14/20 for Graphify's control. The semantic scores above separately +allow leading indentation differences at callable-span starts and missing blank +separators between complete bodies. Both controls also omit the Click class +header, whose identity is already supplied by the verified selected owner; +all initialization, decorator and getter code is present. Every exception and +rejected fact is recorded individually. This is same-agent adjudication with a +separate verifier, not independent human review. + +All 54 native member spans match stored full-span digests and pinned source +bytes, including the returned prefixes of truncated members. Seven members are +omitted by the byte budget across Chi and WalkDir; no member source read fails +in this sample. All 130 returned membership anchors (69 Compass, 61 Graphify) +were checked against declaration lines. Nine saved neighbor request/response +pairs and every source window were verified. Compass reports no neighbor or +transport truncation; no Graphify truncation marker was observed. Neither fact +proves complete graph membership or general relationship precision. + +Native member mode returns 23,082 source bytes in 48,177 stdout bytes. The +shared control returns 27,673 source bytes for Compass and 35,673 for Graphify; +Compass's unresolved Redux subject contributes zero. Neighbor text totals are +26,497 versus 6,980 bytes; full MCP responses total 281,242 versus 7,534 bytes. +These figures exclude previously captured resolver traffic and do not measure +latency or equal computational work. + +Validation passed: formatting, 13 focused query tests, 40 CLI query tests, +60 MCP tests, workspace Clippy, 1,106 workspace tests (2 ignored), 9 product +tests, the product boundary and a final CLI build. Recorded Rust source hashes +match `6d4df994`; the final build matches the evaluated binary. JavaScript/viewer +and extraction/resolution publication gates were not rerun because those +surfaces are unchanged. Version remains 0.3.30. Discovery, metadata, source and +verification-work bounds have native regressions; stale or missing source +status remains explicit. + +The first external collector failed before any public request because of an +invalid hash-helper read bound. Its log and script are retained; the corrected +capture uses a fresh directory. A verifier parser initially included the summary +heading as a member; its failed attempt is also retained. The corrected verifier +passes, including byte-to-line consistency checks. Detailed judgments and hashes +are in `benchmarks/agent_query/member_source_development_review.json`; external +artifacts are under `member-source-02`. The next explanation work needs better +source context and selection, actual responsibility synthesis, and fresh +confirmation. This result does not establish overall superiority. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 2ef40e95ca45972f92696cc5d427c8e9808476dd Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:05:34 -0700 Subject: [PATCH 72/97] audit: register exact source-constrained symbol lookup --- ...exact_symbol_development_registration.json | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 benchmarks/agent_query/exact_symbol_development_registration.json diff --git a/benchmarks/agent_query/exact_symbol_development_registration.json b/benchmarks/agent_query/exact_symbol_development_registration.json new file mode 100644 index 000000000..2fd403433 --- /dev/null +++ b/benchmarks/agent_query/exact_symbol_development_registration.json @@ -0,0 +1,56 @@ +{ + "schema": "compass.exact-symbol-development-registration/1", + "baselineCommit": "5131598e0ee06992c12a233656d2816cfd9cf977", + "scope": "Known five-language development subjects. Prior source and outputs have been inspected. Test explicit exact name/ID search with optional source file, declaration line and node-kind filters; no hidden selector substitutions or held-out claim.", + "sourceQuestions": "benchmarks/agent_query/responsibility_questions_panel_a.json", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "graphRun": "rust-index-receiver-03/run.json", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "tasks": [ + { + "repository": "chi", + "symbol": "Mux", + "file": "mux.go", + "startLine": 21, + "kind": "struct" + }, + { + "repository": "click", + "symbol": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 455, + "kind": "class" + }, + { + "repository": "jsoup", + "symbol": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43, + "kind": "class" + }, + { + "repository": "redux", + "symbol": "createStore", + "file": "src/createStore.ts", + "startLine": 86, + "kind": "function" + }, + { + "repository": "walkdir", + "symbol": "IntoIter", + "file": "src/lib.rs", + "startLine": 566, + "kind": "struct" + } + ], + "productContract": "Exact node ID takes precedence; otherwise normalized exact name or qualified name lookup. All matching candidate records remain visible; file, start line and kind are explicit conjunctive filters. No fuzzy/lexical fallback or inferred ownership/export collapse. The existing candidate cap applies before filtering and truncation remains explicit, even if the filtered prefix has one or zero results. Default ranked search remains unchanged.", + "identityControl": "Both tools receive the same known symbol, repository-relative file, declaration line and expected kind. Compass uses search_symbols exact=true, source_file, start_line, kind with existing limits (256 candidates, 500 nodes, 524288 response bytes). Graphify uses its existing file::symbol get_node; validate returned source identity and source-declaration kind afterward. Do not imply Graphify natively accepts all filters. One call per tool/subject, no retries. Preserve all ambiguity/error/limit outcomes.", + "followup": "For each uniquely source-validated endpoint, repeat the member_source_development_registration symmetric public-neighbor/source-window policy verbatim, with the same 8000-byte source quota and all 20 facts. Keep the earlier 11/20 vs 15/20 workflow and native-member 13/20 scores separate. Capture native declaration explanation for resolved roots separately if used; do not combine scores.", + "negativeControls": "Native fixtures must cover duplicate exact names, same source coordinates with distinct kinds, multiple overloads of the same kind, exact IDs, wrong file/line/kind, no exact match despite lexical hits, truncated exact lookup even after filters, response limits, invalid selectors/filters, and JSON/store parity. Public real-source probes retain Redux function/export ambiguity when kind is omitted and reject a nonexistent line.", + "verification": "Preserve raw argv, MCP requests/responses, graphs, source pins, binary and support hashes. Verify every selected identity against pinned source and every follow-up window against public returned anchors. Existing unscoped resolver payloads must be replayed unchanged; expose any differences.", + "limitations": [ + "Same-agent semantic adjudication, not independent human review.", + "Explicit identity constraints are additional task inputs; do not retroactively replace old unresolved outcomes.", + "This does not improve extraction, synthesize explanations, or prove god-object quality or broad superiority." + ] +} From 9cc6ba9e2b83c8bf3a573cf8dec755092f9c93e9 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:26:13 -0700 Subject: [PATCH 73/97] feat(query): add exact symbol search with explicit source filters --- CHANGELOG.md | 4 + COMPATIBILITY.md | 27 ++ crates/compass-cli/src/code_query_commands.rs | 61 +++- crates/compass-cli/src/help.rs | 2 +- crates/compass-cli/tests/code_query_cli.rs | 70 ++++ crates/compass-mcp/src/code_query.rs | 69 +++- crates/compass-mcp/src/lib.rs | 4 +- crates/compass-mcp/tests/code_query_tools.rs | 64 +++- crates/compass-output/src/agent_query.rs | 28 +- crates/compass-output/tests/agent_query.rs | 48 +++ crates/compass-query/src/code_query.rs | 107 ++++++ crates/compass-query/src/lib.rs | 4 +- crates/compass-query/tests/code_search.rs | 320 ++++++++++++++++++ docs/reference/commands.md | 19 ++ 14 files changed, 805 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0530acf..b12bbec36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Add `search --exact` and MCP `search_symbols` exact mode with optional + source file, declaration line and node-kind filters. Return all bounded exact + matches without lexical fallback; preserve ambiguity and incomplete lookup. + - Add opt-in `explain --source-members` to retrieve callable implementations through recorded containment, including nested types. Share one source-byte budget, retain individual verification status, and report unavailable or diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 69c88819c..3456e4f2e 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -431,6 +431,33 @@ shared JSON output envelopes. The query library exposes `digest_verified` on `--no-source` restores a metadata-only answer; `--source` remains accepted. Ambiguous or unsourced targets do not produce source text. +### Exact symbol search + +`search --exact` and MCP `search_symbols` with `exact: true` use the existing +bounded exact ID/name index without lexical or fuzzy recall. Optional CLI +`--file`, `--line`, `--kind` filters correspond to MCP `source_file`, +`start_line`, `kind`. Filters require exact mode; a line also requires a file. +File strings match stored paths exactly and are not opened or canonicalized. +Selectors and file filters accept 1..4096 non-control bytes; lines are positive +32-bit integers and kinds use the stored node-kind spellings. + +Exact IDs take precedence over name lookup, then must satisfy every supplied +filter. Names use the existing normalization (trim whitespace, trailing `()`, +leading `.`, and lowercase); file paths and IDs remain case-sensitive. All +matching records remain visible, including overloads and export bindings. +Scores are uniformly 1 and order is stable by ID. This mode does not select a +winner or collapse a binding into its declaration. + +The candidate cap applies before filtering. A truncated prefix cannot prove +uniqueness or absence, even when filtering returns one or zero nodes. Node and +response bounds and coverage diagnostics still apply. Exact CLI text requests +do not automatically widen candidate bounds. Default ranked search and the +`compass.query/1` response schema remain unchanged. The query API adds +`search_exact(SearchRequest, ExactSearchFilter)` without changing `SearchRequest`. +Agent View uses typed name normalization when identifying exact search matches. +An empty bounded search without a no-match diagnostic retains `match=unknown` +and partial execution instead of asserting absence or failing view validation. + ### Explanation member source `explain --source-members` replaces the declaration excerpt with callable member diff --git a/crates/compass-cli/src/code_query_commands.rs b/crates/compass-cli/src/code_query_commands.rs index e6960f98e..a29c4f293 100644 --- a/crates/compass-cli/src/code_query_commands.rs +++ b/crates/compass-cli/src/code_query_commands.rs @@ -11,8 +11,8 @@ use compass_output::{ render_code_query_text_page, }; use compass_query::{ - EngineSelection, NaturalQueryIntent, NaturalQueryRequest, QueryError, QueryErrorKind, - open_with_engine, open_with_verified_document, plan_natural_query, + EngineSelection, ExactSearchFilter, NaturalQueryIntent, NaturalQueryRequest, QueryError, + QueryErrorKind, open_with_engine, open_with_verified_document, plan_natural_query, }; use crate::{Outcome, SharedOutputFormat, parse_shared_output_format}; @@ -133,7 +133,10 @@ fn execute_paged( let mut scale = 1_u32; loop { let execution = execute(operation, args, scale, deadline)?; - if !execution.response.truncated || scale >= MAX_PAGE_WIDENING_SCALE { + if !execution.response.truncated + || scale >= MAX_PAGE_WIDENING_SCALE + || args.iter().any(|arg| arg == "--exact") + { return Ok(execution); } scale = scale.saturating_mul(4); @@ -146,6 +149,39 @@ fn execute( page_scale: u32, deadline: Instant, ) -> Result { + let exact = args.iter().any(|arg| arg == "--exact"); + let mut exact_filter = ExactSearchFilter::default(); + for name in ["--file", "--line", "--kind"] { + let present = args + .iter() + .any(|arg| arg == name || arg.starts_with(&format!("{name}="))); + if present && (!exact || operation != "search") { + return Err(format!("{name} requires search --exact")); + } + if present { + let value = option(args, name) + .filter(|value| !value.starts_with("--")) + .ok_or_else(|| format!("{name} requires a value"))?; + match name { + "--file" => exact_filter.source_file = Some(value.to_owned()), + "--line" => { + exact_filter.start_line = Some( + value + .parse() + .map_err(|_| "--line requires a positive integer")?, + ) + } + "--kind" => exact_filter.kind = + Some(serde_json::from_value(serde_json::json!(value)).map_err( + |_| "--kind requires a stored node kind such as function, class, or struct", + )?), + _ => {} + } + } + } + if exact && operation != "search" { + return Err("--exact requires search".to_owned()); + } let positional = positional(args); let graph_option = option(args, "--graph"); let revision = option(args, "--at"); @@ -257,12 +293,16 @@ fn execute( } "search" => { let query = required(&positional, 0, "search ")?.to_owned(); - let response = engine - .search(SearchRequest { - query: query.clone(), - limits, - }) - .map_err(query_error)?; + let request = SearchRequest { + query: query.clone(), + limits, + }; + let response = if exact { + engine.search_exact(request, exact_filter) + } else { + engine.search(request) + } + .map_err(query_error)?; (response, None, vec![(AgentOperandRole::Query, query)]) } "callers" | "callees" | "impact" => { @@ -430,6 +470,9 @@ fn positional(args: &[String]) -> Vec { "--text-budget", "--cursor", "--timeout-ms", + "--file", + "--line", + "--kind", ]; let mut values = Vec::new(); let mut skip = false; diff --git a/crates/compass-cli/src/help.rs b/crates/compass-cli/src/help.rs index 92d81ca85..f8d446b02 100644 --- a/crates/compass-cli/src/help.rs +++ b/crates/compass-cli/src/help.rs @@ -320,7 +320,7 @@ const PAGES: &[Page] = &[ "search", "Search typed code symbols by name", ["compass search [OPTIONS]"], - "Arguments:\n Symbol name or qualified name\n\nOptions:\n --graph Typed graph [default: compass-out/graph.json]\n --program Optional Program IR enrichment\n --cache Query-index cache directory\n --engine Graph storage engine [default: default]\n --max-candidates Candidate bound [default: 64]\n --format Output format [default: text]\n --text-budget Approximate tokens per text page [default: 2000]\n --cursor Continue the same text result (text only)\n --timeout-ms Query deadline in milliseconds [default: 60000; max: 600000]\n --brief Emit compass.query.agent-view.brief/1 (agent-json only)\n\nExamples:\n compass search PaymentService\n compass search checkout --format json\n compass search PaymentService --text-budget 800\n\nNotes:\n Search uses the versioned compass.query/1 response contract in all formats. Text output is paged: the footer carries a cursor that continues the same deterministic result at the same page budget." + "Arguments:\n Symbol name or qualified name\n\nOptions:\n --exact Exact ID or normalized name only; no lexical fallback\n --file Exact stored source path (requires --exact)\n --line Declaration start line (requires --exact and --file)\n --kind Stored node kind, e.g. function (requires --exact)\n --graph Typed graph [default: compass-out/graph.json]\n --program Optional Program IR enrichment\n --cache Query-index cache directory\n --engine Graph storage engine [default: default]\n --max-candidates Candidate bound [default: 64]\n --format Output format [default: text]\n --text-budget Approximate tokens per text page [default: 2000]\n --cursor Continue the same text result (text only)\n --timeout-ms Query deadline in milliseconds [default: 60000; max: 600000]\n --brief Emit compass.query.agent-view.brief/1 (agent-json only)\n\nExamples:\n compass search PaymentService\n compass search checkout --format json\n compass search PaymentService --text-budget 800\n\nNotes:\n Exact mode retains all matches. Candidate bounds apply before filters; truncation never proves uniqueness or absence. Exact text results do not automatically widen bounds.\n Search uses the versioned compass.query/1 response contract in all formats. Text output is paged: the footer carries a cursor that continues the same deterministic result at the same page budget." ), page!( "callers", diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 4498139cf..157ed5898 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -2375,3 +2375,73 @@ fn explain_member_source_reaches_implementations_outside_type_declarations() ); Ok(()) } + +#[test] +fn exact_search_cli_filters_explicitly_and_preserves_bounded_ambiguity() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let graph_path = support::write_typed_graph(directory.path())?; + let mut graph = GraphDocument::load(&graph_path)?; + let target = graph + .nodes + .iter() + .find(|n| n.name == "Target") + .ok_or("target")? + .clone(); + let source = target.source.as_ref().ok_or("target source")?; + let mut export = target.clone(); + export.id = "duplicate:export".to_owned(); + export.kind = NodeKind::Export; + graph.nodes.push(export); + std::fs::write(&graph_path, serde_json::to_vec(&graph)?)?; + let execute = |extra: &[&str]| { + let mut args = vec![ + OsString::from("search"), + OsString::from("Target"), + OsString::from("--graph"), + graph_path.as_os_str().to_owned(), + ]; + args.extend(extra.iter().map(OsString::from)); + run(Frontend::Compass, args) + }; + let line = source.start_line.to_string(); + let result = execute(&[ + "--exact", + "--file", + &source.file, + "--line", + &line, + "--kind", + "function", + "--format", + "json", + ]); + assert_eq!(result.code, 0, "{}", result.stderr); + let body: Value = serde_json::from_str(&result.stdout)?; + assert_eq!(body["truncated"], false); + assert_eq!(body["results"].as_array().ok_or("results")?.len(), 1); + assert_eq!(body["results"][0]["nodeId"], target.id); + let ambiguous = execute(&["--exact", "--format", "json"]); + let body: Value = serde_json::from_str(&ambiguous.stdout)?; + assert_eq!(body["results"].as_array().ok_or("results")?.len(), 2); + for format in ["text", "agent-json"] { + let bounded = execute(&["--exact", "--max-candidates", "1", "--format", format]); + assert_eq!(bounded.code, 0, "{}", bounded.stderr); + assert!( + bounded.stdout.contains("bounded_truncation"), + "{}", + bounded.stdout + ); + } + for args in [ + vec!["--file", &source.file], + vec!["--exact", "--file"], + vec!["--exact", "--kind", "unknown"], + vec!["--exact", "--line", "0"], + vec!["--exact", "--line", "1"], + vec!["--exact", "--file", "--kind", "function"], + ] { + assert_ne!(execute(&args).code, 0, "{args:?}"); + } + Ok(()) +} diff --git a/crates/compass-mcp/src/code_query.rs b/crates/compass-mcp/src/code_query.rs index c984dac0b..15890e5b1 100644 --- a/crates/compass-mcp/src/code_query.rs +++ b/crates/compass-mcp/src/code_query.rs @@ -3,7 +3,7 @@ use compass_model::query_contract::{ DiscoveryQueryRequest, DiscoveryQueryResponse, DiscoveryScope, DiscoveryScopeKind, DiscoveryTraversal, ExploreRequest, ImpactRequest, NodeTrailRequest, SearchRequest, }; -use compass_query::{CodeQueryEngine, NaturalQueryRequest, QueryErrorKind}; +use compass_query::{CodeQueryEngine, ExactSearchFilter, NaturalQueryRequest, QueryErrorKind}; use serde_json::{Map, Value, json}; pub(super) fn schema(required: &[&str]) -> Value { @@ -45,11 +45,29 @@ pub(super) fn schema(required: &[&str]) -> Value { }) } +pub(super) fn search_schema() -> Value { + let mut result = schema(&["query"]); + result["properties"]["exact"] = json!({"type":"boolean","default":false,"description":"Only exact ID or normalized name matches; no lexical fallback."}); + result["properties"]["source_file"] = json!({"type":"string","minLength":1,"maxLength":4096,"description":"Exact stored source path; requires exact=true."}); + result["properties"]["start_line"] = json!({"type":"integer","minimum":1,"maximum":u32::MAX,"description":"Declaration start line; requires exact=true and source_file."}); + result["properties"]["kind"] = json!({"type":"string","description":"Stored node kind (for example function, class, struct); requires exact=true."}); + result +} + pub(super) fn invoke_with_engine( name: &str, arguments: &Map, engine: &CodeQueryEngine, ) -> Result { + if name != "search_symbols" + && ["exact", "source_file", "start_line", "kind"] + .iter() + .any(|key| arguments.contains_key(*key)) + { + return Err(super::InvocationError::InvalidParams( + "exact symbol filters require search_symbols".to_owned(), + )); + } let limits = limits(arguments)?; match name { "query_graph" => engine.query_natural(NaturalQueryRequest { @@ -57,10 +75,51 @@ pub(super) fn invoke_with_engine( include_heuristic: false, limits, }), - "search_symbols" => engine.search(SearchRequest { - query: required_string(arguments, "query")?, - limits, - }), + "search_symbols" => { + let request = SearchRequest { + query: required_string(arguments, "query")?, + limits, + }; + let exact = boolean(arguments, "exact")?; + if !exact + && ["source_file", "start_line", "kind"] + .iter() + .any(|key| arguments.contains_key(*key)) + { + return Err(super::InvocationError::InvalidParams( + "source_file, start_line and kind require exact=true".to_owned(), + )); + } + if exact { + let source_file = arguments + .get("source_file") + .map(|_| required_string(arguments, "source_file")) + .transpose()?; + let start_line = arguments + .get("start_line") + .map(|_| u32_value(arguments, "start_line", 0)) + .transpose()?; + let kind = arguments + .get("kind") + .map(|value| { + serde_json::from_value(value.clone()).map_err(|_| { + "kind must be a stored node kind such as function, class, or struct" + .to_owned() + }) + }) + .transpose()?; + engine.search_exact( + request, + ExactSearchFilter { + source_file, + start_line, + kind, + }, + ) + } else { + engine.search(request) + } + } "get_callers" => engine.callers(CallRequest { symbol: required_string(arguments, "symbol")?, include_heuristic: boolean(arguments, "include_heuristic")?, diff --git a/crates/compass-mcp/src/lib.rs b/crates/compass-mcp/src/lib.rs index ade085169..09ab58ef7 100644 --- a/crates/compass-mcp/src/lib.rs +++ b/crates/compass-mcp/src/lib.rs @@ -1311,8 +1311,8 @@ fn tool_specs() -> Vec { let mut specs = vec![ tool( "search_symbols", - "Search Compass code symbols with the trusted FTS5 index.", - code_query::schema(&["query"]), + "Search Compass code symbols. Set exact=true for bounded exact ID/name candidates with optional source_file, start_line and kind filters; all matching records and truncation remain visible.", + code_query::search_schema(), ), tool( "get_callers", diff --git a/crates/compass-mcp/tests/code_query_tools.rs b/crates/compass-mcp/tests/code_query_tools.rs index 4b6b494ab..588b4a1c3 100644 --- a/crates/compass-mcp/tests/code_query_tools.rs +++ b/crates/compass-mcp/tests/code_query_tools.rs @@ -175,7 +175,8 @@ fn invoke(server: &CompassMcp, name: &str, arguments: Value) -> Result(&output)?; + let envelope = serde_json::from_str::(&output) + .map_err(|error| format!("invalid {name} response: {error}: {output}"))?; assert_eq!(envelope["schema"], "compass.mcp.tool-result/1"); assert_eq!(envelope["transportTruncation"]["truncated"], false); Ok(envelope["result"].clone()) @@ -675,3 +676,64 @@ async fn mcp_code_queries_publish_structured_content_and_protocol_errors() server_task.await?.map_err(std::io::Error::other)?; Ok(()) } + +#[test] +fn exact_symbol_tool_preserves_filters_and_rejects_incomplete_requests() +-> Result<(), Box> { + let directory = tempfile::tempdir()?; + let path = write_typed_graph(directory.path())?; + let mut graph = GraphDocument::load(&path)?; + let target = graph + .nodes + .iter() + .find(|n| n.name == "Target") + .ok_or("target")? + .clone(); + let source = target.source.as_ref().ok_or("source")?; + let mut export = target.clone(); + export.id = "export:target".to_owned(); + export.kind = NodeKind::Export; + graph.nodes.push(export); + fs::write(&path, serde_json::to_vec(&graph)?)?; + let server = CompassMcp::new(path); + let result = invoke( + &server, + "search_symbols", + json!({"query":"Target", "exact":true, "source_file":source.file, "start_line":source.start_line,"kind":"function"}), + )?; + assert_eq!(result["truncated"], false); + assert_eq!(result["results"].as_array().ok_or("results")?.len(), 1); + assert_eq!(result["results"][0]["nodeId"], target.id); + let ambiguity = invoke( + &server, + "search_symbols", + json!({"query":"Target", "exact":true}), + )?; + assert_eq!(ambiguity["results"].as_array().ok_or("results")?.len(), 2); + let bounded = invoke( + &server, + "search_symbols", + json!({"query":"Target", "exact":true,"max_candidates":1,"kind":"class"}), + )?; + assert_eq!(bounded["truncated"], true); + assert_eq!(bounded["results"], json!([])); + for args in [ + json!({"query":"Target","kind":"function"}), + json!({"query":"Target","exact":"true"}), + json!({"query":"Target","exact":true,"start_line":1}), + json!({"query":"Target","exact":true,"source_file":null}), + json!({"query":"Target","exact":true,"kind":"unknown"}), + json!({"query":"Target","exact":true,"source_file":"src/lib.rs","start_line":0}), + ] { + let output = server.invoke("search_symbols", args.as_object().ok_or("args")?.clone()); + assert!( + output.starts_with("Error executing search_symbols:"), + "{output}" + ); + assert!( + !output.contains("\"schema\":\"compass.query/1\""), + "{output}" + ); + } + Ok(()) +} diff --git a/crates/compass-output/src/agent_query.rs b/crates/compass-output/src/agent_query.rs index d1fdff41a..3205dd018 100644 --- a/crates/compass-output/src/agent_query.rs +++ b/crates/compass-output/src/agent_query.rs @@ -2595,13 +2595,26 @@ fn code_match_state( if query.is_some_and(|query| { response.results.first().is_some_and(|hit| { nodes.get(&hit.node_id).is_some_and(|node| { - node.id == query || node.name == query || node.qualified_name == query + node.id == query + || normalize_code_query_symbol(&node.name) + == normalize_code_query_symbol(query) + || normalize_code_query_symbol(&node.qualified_name) + == normalize_code_query_symbol(query) }) }) }) { AgentMatch::Exact } else if response.results.is_empty() { - AgentMatch::None + if response.truncated + || has_diagnostic( + &response.diagnostics, + QueryDiagnosticCode::BoundedTruncation, + ) + { + AgentMatch::Unknown + } else { + AgentMatch::None + } } else { AgentMatch::Fuzzy } @@ -2635,7 +2648,9 @@ fn code_result_state( { return AgentResultState::NoPath; } - if operation == AgentOperation::Search && match_state == AgentMatch::Fuzzy { + if operation == AgentOperation::Search + && matches!(match_state, AgentMatch::Fuzzy | AgentMatch::Unknown) + { AgentResultState::Candidates } else { AgentResultState::Answered @@ -2696,6 +2711,13 @@ fn answer_for_code( .unwrap_or_else(|| requested.clone()); let headline = match context.operation { AgentOperation::Search => match result_state { + AgentResultState::Candidates + if match_state == AgentMatch::Unknown && response.results.is_empty() => + { + format!( + "Search stopped at its bound before a match or absence could be established for \"{requested}\"." + ) + } AgentResultState::NoMatch => { format!("No exact match for \"{requested}\"; fallback candidates are shown.") } diff --git a/crates/compass-output/tests/agent_query.rs b/crates/compass-output/tests/agent_query.rs index 69e2351e4..a04ba64dc 100644 --- a/crates/compass-output/tests/agent_query.rs +++ b/crates/compass-output/tests/agent_query.rs @@ -972,3 +972,51 @@ fn unresolved_relationship_answers_never_speak_for_another_symbol() -> Result<() ); Ok(()) } + +#[test] +fn bounded_empty_search_is_unknown_instead_of_a_no_match_claim() -> Result<(), Box> { + for diagnostic_only in [false, true] { + let mut result = response(CodeQueryOperation::Search); + result.truncated = !diagnostic_only; + result.diagnostics.push(QueryDiagnostic { + code: QueryDiagnosticCode::BoundedTruncation, + message: "Exact lookup stopped before filtering was complete".to_owned(), + node_id: None, + path: None, + }); + let view = build_code_query_view( + &result, + context(AgentOperation::Search) + .with_operand(compass_output::AgentOperandRole::Query, "Subject"), + )?; + assert_eq!(view.status.match_state, AgentMatch::Unknown); + assert_eq!(view.status.result_state, AgentResultState::Candidates); + assert_eq!(view.status.source_execution, AgentExecution::Partial); + assert!(view.primary_results.is_empty()); + let text = render_agent_query_text(&view)?; + assert!(text.contains("before a match or absence could be established")); + assert!(!view.caveats.iter().any(|c| c.code == "no_match")); + } + Ok(()) +} + +#[test] +fn search_exact_match_display_uses_typed_name_normalization() -> Result<(), Box> { + let mut result = response(CodeQueryOperation::Search); + result + .nodes + .push(node("id:subject", ".Subject()", &anchor("src/lib.rs", 1))); + result.results.push(SearchHit { + node_id: "id:subject".to_owned(), + score: 1.0, + matched_fields: vec!["name".to_owned()], + }); + let view = build_code_query_view( + &result, + context(AgentOperation::Search) + .with_operand(compass_output::AgentOperandRole::Query, " SUBJECT "), + )?; + assert_eq!(view.status.match_state, AgentMatch::Exact); + assert_eq!(view.status.result_state, AgentResultState::Answered); + Ok(()) +} diff --git a/crates/compass-query/src/code_query.rs b/crates/compass-query/src/code_query.rs index c0e648e00..077079f2c 100644 --- a/crates/compass-query/src/code_query.rs +++ b/crates/compass-query/src/code_query.rs @@ -307,6 +307,15 @@ const IMPACT_KINDS: &[EdgeKind] = &[ EdgeKind::Renders, ]; +/// Optional conjunctive filters for exact symbol lookup. Paths are compared to +/// stored repository-relative paths; they are never opened or canonicalized. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct ExactSearchFilter { + pub source_file: Option, + pub start_line: Option, + pub kind: Option, +} + pub struct CodeQueryEngine { pub(crate) backend: CodeGraphBackend, pub(crate) program: Option, @@ -1568,6 +1577,104 @@ impl CodeQueryEngine { self.search_instrumented(request, &mut QueryInstrumentation::default()) } + /// Return every bounded exact ID/name candidate satisfying explicit filters. + /// Name normalization matches typed symbol resolution. No lexical fallback, + /// ranking-based selection or export-binding elimination is performed. + /// The candidate bound applies before filtering: a filtered prefix never + /// establishes uniqueness or absence when exact lookup was truncated. + pub fn search_exact( + &self, + request: SearchRequest, + filter: ExactSearchFilter, + ) -> Result { + self.check_deadline()?; + validate_limits(&request.limits)?; + let valid_text = |value: &str| { + !value.trim().is_empty() && value.len() <= 4096 && !value.chars().any(char::is_control) + }; + if !valid_text(&request.query) + || filter + .source_file + .as_deref() + .is_some_and(|value| !valid_text(value)) + || filter.start_line == Some(0) + || (filter.start_line.is_some() && filter.source_file.is_none()) + { + return Err(QueryError::new( + QueryErrorKind::InvalidParameter, + "invalid_exact_search", + "exact search requires 1..4096 non-control selector/file bytes; start line must be positive and requires a source file", + )); + } + let bound = usize::try_from(request.limits.max_candidates).unwrap_or(usize::MAX); + let exact_id = self.backend.node_by_id(&request.query)?; + let id_match = exact_id.is_some(); + let (mut candidates, truncated) = if let Some(node) = exact_id { + (vec![node], false) + } else { + self.backend + .nodes_by_normalized_name(&normalize_symbol(&request.query), bound)? + }; + self.check_deadline()?; + candidates.retain(|node| { + filter.kind.is_none_or(|kind| node.kind == kind) + && filter.source_file.as_deref().is_none_or(|file| { + node.source + .as_ref() + .is_some_and(|source| source.file == file) + }) + && filter.start_line.is_none_or(|line| { + node.source + .as_ref() + .is_some_and(|source| source.start_line == line) + }) + }); + candidates.sort_by(|a, b| a.id.cmp(&b.id)); + let mut response = CodeQueryResponse::empty(CodeQueryOperation::Search, request.limits); + response.truncated = truncated; + if truncated { + response.diagnostics.push(QueryDiagnostic { + code: QueryDiagnosticCode::BoundedTruncation, + message: format!("Exact-name lookup exceeded {bound} candidates before filtering; returned matches do not establish uniqueness or absence"), + node_id: None, + path: None, + }); + } else if candidates.is_empty() { + response.diagnostics.push(QueryDiagnostic { + code: QueryDiagnosticCode::NoMatch, + message: "No exact symbol satisfies the supplied filters".to_owned(), + node_id: None, + path: None, + }); + } + let max_nodes = usize::try_from(response.limits.max_nodes).unwrap_or(usize::MAX); + if candidates.len() > max_nodes { + response.truncated = true; + candidates.truncate(max_nodes); + } + for node in candidates { + let matched_fields = if id_match { + vec!["id".to_owned()] + } else { + [ + ("name", &node.name), + ("qualified_name", &node.qualified_name), + ] + .into_iter() + .filter(|(_, value)| normalize_symbol(value) == normalize_symbol(&request.query)) + .map(|(field, _)| field.to_owned()) + .collect() + }; + response.results.push(SearchHit { + node_id: node.id.clone(), + score: 1.0, + matched_fields, + }); + response.nodes.push(query_node(&node)); + } + self.finish_response(&mut response) + } + pub(crate) fn search_instrumented( &self, request: SearchRequest, diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index 65a456551..0aed34a76 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -28,7 +28,9 @@ mod traversal; pub use affected::{DEFAULT_AFFECTED_RELATIONS, affected_nodes, format_affected, resolve_seed}; pub use benchmark::{BenchmarkQuestion, BenchmarkResult, format_benchmark, run_benchmark}; -pub use code_query::{CodeQueryEngine, normalize_symbol as normalize_code_query_symbol}; +pub use code_query::{ + CodeQueryEngine, ExactSearchFilter, normalize_symbol as normalize_code_query_symbol, +}; pub use cql::{ CacheStats, ExplainPlan, OperatorProfile, PlanCache, PlanCacheConfig, QueryError, QueryErrorKind, QueryLimits, QueryProfile, QueryRequest, QueryResult, execute, diff --git a/crates/compass-query/tests/code_search.rs b/crates/compass-query/tests/code_search.rs index 1ed98edc5..5edfc1f9a 100644 --- a/crates/compass-query/tests/code_search.rs +++ b/crates/compass-query/tests/code_search.rs @@ -126,3 +126,323 @@ fn search_discloses_partial_publication_coverage() -> Result<(), Box Result> { + let directory = tempfile::tempdir()?; + let path = directory.path().join("graph.json"); + support::write_graph(&path)?; + let mut graph = GraphDocument::load(&path)?; + let template = graph.nodes.first().ok_or("fixture node missing")?.clone(); + let mut other_file = graph + .graph + .files + .first() + .ok_or("fixture file missing")? + .clone(); + other_file.path = "other.rs".to_owned(); + other_file.id = compass_model::identity::file_id("other.rs"); + graph.graph.files.push(other_file); + graph.nodes.clear(); + graph.links.clear(); + for (id, name, kind, file, line) in [ + ( + "a:declaration", + "Target()", + compass_model::code_graph::NodeKind::Function, + "src/lib.rs", + 10, + ), + ( + "b:export", + "Target", + compass_model::code_graph::NodeKind::Export, + "src/lib.rs", + 10, + ), + ( + "c:overload", + "Target()", + compass_model::code_graph::NodeKind::Function, + "src/lib.rs", + 20, + ), + ( + "d:other", + "Target()", + compass_model::code_graph::NodeKind::Function, + "other.rs", + 10, + ), + ] { + let mut node = template.clone(); + node.id = id.to_owned(); + node.name = name.to_owned(); + node.qualified_name = format!("Scope.{name}"); + node.kind = kind; + let source = node.source.as_mut().ok_or("fixture source missing")?; + source.file = file.to_owned(); + source.start_line = line; + source.end_line = line; + graph.nodes.push(node); + } + for i in 0..300 { + let mut node = template.clone(); + node.id = format!("noise:{i:03}"); + node.name = format!("TargetAdapter{i}"); + node.qualified_name = format!("Other.{}", node.name); + graph.nodes.push(node); + } + Ok(graph) +} + +fn exact_search_engines( + directory: &std::path::Path, + graph: GraphDocument, +) -> Result, Box> { + let path = directory.join("graph.json"); + std::fs::write(&path, serde_json::to_vec(&graph)?)?; + let json = compass_query::open_with_engine( + &path, + None, + &directory.join("json-cache"), + compass_query::EngineSelection::Json, + )?; + let direct = compass_query::open_with_document( + graph.clone(), + &path, + None, + &directory.join("direct-cache"), + )?; + let store = compass_store::SqliteStore::open(directory.join("store.sqlite"))?; + let prepared = compass_graph::GraphSnapshotBuilder::new().prepare(&store, &graph)?; + compass_graph::GraphSnapshotBuilder::new().activate(&store, &prepared)?; + let stored = + compass_query::open_with_store(&store, &path, None, &directory.join("store-cache"))?; + Ok(vec![json, direct, stored]) +} + +#[test] +fn exact_search_avoids_lexical_overflow_and_keeps_explicit_identity_constraints() +-> Result<(), Box> { + use compass_model::code_graph::NodeKind; + use compass_query::ExactSearchFilter; + let directory = tempfile::tempdir()?; + let engines = exact_search_engines(directory.path(), exact_search_graph()?)?; + let request = || SearchRequest { + query: "Target".to_owned(), + limits: CodeQueryLimits { + max_candidates: 256, + max_nodes: 500, + ..CodeQueryLimits::default() + }, + }; + let mut responses = Vec::new(); + for engine in engines { + assert!(engine.search(request())?.truncated); + let exact = engine.search_exact(request(), ExactSearchFilter::default())?; + assert!(!exact.truncated); + assert_eq!(exact.results.len(), 4); + let filter = ExactSearchFilter { + source_file: Some("src/lib.rs".to_owned()), + start_line: Some(10), + kind: None, + }; + let ambiguity = engine.search_exact(request(), filter.clone())?; + assert!(!ambiguity.truncated); + assert_eq!( + ambiguity + .nodes + .iter() + .map(|n| n.id.as_str()) + .collect::>(), + ["a:declaration", "b:export"] + ); + let selected = engine.search_exact( + request(), + ExactSearchFilter { + kind: Some(NodeKind::Function), + ..filter.clone() + }, + )?; + assert!(!selected.truncated); + assert_eq!(selected.results.len(), 1); + assert_eq!(selected.results[0].node_id, "a:declaration"); + let miss = engine.search_exact( + request(), + ExactSearchFilter { + start_line: Some(11), + ..filter.clone() + }, + )?; + assert!(!miss.truncated && miss.results.is_empty()); + assert!( + miss.diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::NoMatch) + ); + for filter in [ + ExactSearchFilter { + source_file: Some("missing.rs".to_owned()), + ..filter.clone() + }, + ExactSearchFilter { + kind: Some(NodeKind::Class), + ..filter + }, + ] { + let miss = engine.search_exact(request(), filter)?; + assert!(!miss.truncated && miss.results.is_empty()); + } + let lexical_only = engine.search_exact( + SearchRequest { + query: "Adapter".to_owned(), + ..request() + }, + ExactSearchFilter::default(), + )?; + assert!(lexical_only.results.is_empty() && !lexical_only.truncated); + responses.push(serde_json::to_value(selected)?); + } + assert!(responses.windows(2).all(|pair| pair[0] == pair[1])); + Ok(()) +} + +#[test] +fn exact_search_limits_never_turn_a_filtered_prefix_into_proof() +-> Result<(), Box> { + use compass_query::ExactSearchFilter; + let directory = tempfile::tempdir()?; + for engine in exact_search_engines(directory.path(), exact_search_graph()?)? { + let request = || SearchRequest { + query: "Target".to_owned(), + limits: CodeQueryLimits { + max_candidates: 1, + ..CodeQueryLimits::default() + }, + }; + for file in ["src/lib.rs", "other.rs", "missing.rs"] { + let r = engine.search_exact( + request(), + ExactSearchFilter { + source_file: Some(file.to_owned()), + ..ExactSearchFilter::default() + }, + )?; + assert!(r.truncated); + assert!( + !r.diagnostics + .iter() + .any(|d| d.code == QueryDiagnosticCode::NoMatch) + ); + assert!( + r.diagnostics + .iter() + .any(|d| d.message.contains("before filtering")) + ); + } + let bounded = engine.search_exact( + SearchRequest { + limits: CodeQueryLimits { + max_nodes: 1, + ..CodeQueryLimits::default() + }, + ..request() + }, + ExactSearchFilter::default(), + )?; + assert!(bounded.truncated && bounded.nodes.len() == 1 && bounded.results.len() == 1); + let id = engine.search_exact( + SearchRequest { + query: "a:declaration".to_owned(), + ..request() + }, + ExactSearchFilter::default(), + )?; + assert!(!id.truncated && id.results.len() == 1); + let constrained_id = engine.search_exact( + SearchRequest { + query: "a:declaration".to_owned(), + ..request() + }, + ExactSearchFilter { + source_file: Some("other.rs".to_owned()), + ..ExactSearchFilter::default() + }, + )?; + assert!(!constrained_id.truncated && constrained_id.results.is_empty()); + assert!( + engine + .search_exact( + SearchRequest { + limits: CodeQueryLimits { + max_response_bytes: 1, + ..CodeQueryLimits::default() + }, + ..request() + }, + ExactSearchFilter::default() + ) + .is_err() + ); + } + Ok(()) +} + +#[test] +fn exact_search_retains_same_coordinate_overloads_and_rejects_invalid_filters() +-> Result<(), Box> { + use compass_query::ExactSearchFilter; + let directory = tempfile::tempdir()?; + let mut graph = exact_search_graph()?; + let mut duplicate = graph.nodes[0].clone(); + duplicate.id = "a:second-declaration".to_owned(); + graph.nodes.push(duplicate); + graph.nodes.reverse(); + for engine in exact_search_engines(directory.path(), graph)? { + let request = || SearchRequest { + query: " .TARGET() ".to_owned(), + limits: CodeQueryLimits::default(), + }; + let filter = ExactSearchFilter { + source_file: Some("src/lib.rs".to_owned()), + start_line: Some(10), + kind: Some(compass_model::code_graph::NodeKind::Function), + }; + let result = engine.search_exact(request(), filter.clone())?; + assert!(!result.truncated && result.results.len() == 2); + for bad in [ + ExactSearchFilter { + source_file: None, + ..filter.clone() + }, + ExactSearchFilter { + start_line: Some(0), + ..filter.clone() + }, + ExactSearchFilter { + source_file: Some("a\nb".to_owned()), + ..filter.clone() + }, + ExactSearchFilter { + source_file: Some("x".repeat(4097)), + ..filter.clone() + }, + ] { + assert!(engine.search_exact(request(), bad).is_err()); + } + for bad in ["".to_owned(), "a\nb".to_owned(), "x".repeat(4097)] { + assert!( + engine + .search_exact( + SearchRequest { + query: bad, + ..request() + }, + filter.clone() + ) + .is_err() + ); + } + } + Ok(()) +} diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 8db7ad6d5..84846ae7a 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -421,6 +421,25 @@ compass explore "" ... [--format text|agent-json|json] compass node "" "" [--format text|agent-json|json] ``` +When the symbol name and source location are known, use exact lookup: + +```bash +compass search createStore --exact --file src/createStore.ts --line 86 --kind function --format json +``` + +The file, line and kind constraints are optional and conjunctive. A line requires +a file; all three filters require `--exact`. File paths match the stored +repository-relative spelling. Exact IDs take precedence; names use Compass's +normalized exact comparison. Every matching record is returned, including +overloads and export bindings, with no lexical fallback or guessed winner. +The candidate bound applies before filtering: a truncated one-result response +does not prove uniqueness, and a truncated empty response does not prove +absence. Exact text requests keep the supplied bounds without automatic widening. + +MCP exposes the same operation through `search_symbols` with `exact: true` and +optional `source_file`, `start_line` and `kind` fields. The raw response stays +`compass.query/1`; ordinary ranked search remains the default. + `node` searches directed, weighted trails within `--max-depth`. It retains shorter and cheaper prefixes when either can affect reachability within that hop limit. Node and edge work limits still apply: a truncated result is not From 7aef6a0c41dd82d3db0a1a74aff1d5c7f956c363 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:27:30 -0700 Subject: [PATCH 74/97] audit: compare constrained symbol lookup and source evidence --- .../exact_symbol_development_review.json | 3064 +++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 90 + 2 files changed, 3154 insertions(+) create mode 100644 benchmarks/agent_query/exact_symbol_development_review.json diff --git a/benchmarks/agent_query/exact_symbol_development_review.json b/benchmarks/agent_query/exact_symbol_development_review.json new file mode 100644 index 000000000..f31ba3de1 --- /dev/null +++ b/benchmarks/agent_query/exact_symbol_development_review.json @@ -0,0 +1,3064 @@ +{ + "schema": "compass.exact-symbol-development-review/1", + "registration": "benchmarks/agent_query/exact_symbol_development_registration.json", + "registrationSha256": "78781c22e1357515e9cd4e1093ecabe0fc1263262fae3a4fee1b0bf02a8086f2", + "productCommit": "9cc6ba9e2b83c8bf3a573cf8dec755092f9c93e9", + "evaluatedBinarySha256": "2cc997ff293da1900a1c5e43175930862c19e83c44bf0ef4a160b1ea3927dab9", + "scope": "Known five-language development subjects. Prior source and outputs have been inspected. Test explicit exact name/ID search with optional source file, declaration line and node-kind filters; no hidden selector substitutions or held-out claim.", + "summary": { + "compass": { + "subjects": 5, + "resolvedSubjects": 5, + "facts": 20, + "sourceEvidenceFacts": 14, + "literalWitnessFacts": 13, + "explicitNativeFacts": 0, + "sourceBytes": 35673, + "neighborTextBytes": 33061, + "neighborWireBytes": 372829, + "resolverTextBytes": 3039, + "resolverWireBytes": 21983 + }, + "graphify": { + "subjects": 5, + "resolvedSubjects": 5, + "facts": 20, + "sourceEvidenceFacts": 15, + "literalWitnessFacts": 14, + "explicitNativeFacts": 0, + "sourceBytes": 35673, + "neighborTextBytes": 6980, + "neighborWireBytes": 7534, + "resolverTextBytes": 619, + "resolverWireBytes": 1094 + } + }, + "results": [ + { + "repository": "chi", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 476, + "expected": "\tmethod, ok := methodMap[rctx.RouteMethod]", + "returned": null + }, + { + "line": 477, + "expected": "\tif !ok {", + "returned": null + }, + { + "line": 478, + "expected": "\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)", + "returned": null + }, + { + "line": 479, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 480, + "expected": "\t}", + "returned": null + }, + { + "line": 481, + "expected": "", + "returned": null + }, + { + "line": 482, + "expected": "\t// Find the route", + "returned": null + }, + { + "line": 483, + "expected": "\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {", + "returned": null + }, + { + "line": 484, + "expected": "\t\t// Set http.Request path values from our request context", + "returned": null + }, + { + "line": 485, + "expected": "\t\tfor i, key := range rctx.URLParams.Keys {", + "returned": null + }, + { + "line": 486, + "expected": "\t\t\tvalue := rctx.URLParams.Values[i]", + "returned": null + }, + { + "line": 487, + "expected": "\t\t\tr.SetPathValue(key, value)", + "returned": null + }, + { + "line": 488, + "expected": "\t\t}", + "returned": null + }, + { + "line": 489, + "expected": "\t\tr.Pattern = rctx.RoutePattern()", + "returned": null + }, + { + "line": 490, + "expected": "", + "returned": null + }, + { + "line": 491, + "expected": "\t\th.ServeHTTP(w, r)", + "returned": null + }, + { + "line": 492, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 493, + "expected": "\t}", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 13154, + "neighborWireBytes": 147275, + "returnedMembershipRows": 33, + "windows": [ + { + "file": "mux.go", + "startLine": 63, + "lastReturnedLine": 99, + "sourceBytes": 1373, + "partial": false + }, + { + "file": "mux.go", + "startLine": 100, + "lastReturnedLine": 108, + "sourceBytes": 333, + "partial": false + }, + { + "file": "mux.go", + "startLine": 109, + "lastReturnedLine": 120, + "sourceBytes": 386, + "partial": false + }, + { + "file": "mux.go", + "startLine": 121, + "lastReturnedLine": 126, + "sourceBytes": 217, + "partial": false + }, + { + "file": "mux.go", + "startLine": 127, + "lastReturnedLine": 136, + "sourceBytes": 356, + "partial": false + }, + { + "file": "mux.go", + "startLine": 137, + "lastReturnedLine": 142, + "sourceBytes": 241, + "partial": false + }, + { + "file": "mux.go", + "startLine": 143, + "lastReturnedLine": 148, + "sourceBytes": 230, + "partial": false + }, + { + "file": "mux.go", + "startLine": 149, + "lastReturnedLine": 154, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 155, + "lastReturnedLine": 160, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 161, + "lastReturnedLine": 166, + "sourceBytes": 227, + "partial": false + }, + { + "file": "mux.go", + "startLine": 167, + "lastReturnedLine": 172, + "sourceBytes": 228, + "partial": false + }, + { + "file": "mux.go", + "startLine": 173, + "lastReturnedLine": 178, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 179, + "lastReturnedLine": 184, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 185, + "lastReturnedLine": 190, + "sourceBytes": 220, + "partial": false + }, + { + "file": "mux.go", + "startLine": 191, + "lastReturnedLine": 196, + "sourceBytes": 224, + "partial": false + }, + { + "file": "mux.go", + "startLine": 197, + "lastReturnedLine": 202, + "sourceBytes": 242, + "partial": false + }, + { + "file": "mux.go", + "startLine": 203, + "lastReturnedLine": 222, + "sourceBytes": 579, + "partial": false + }, + { + "file": "mux.go", + "startLine": 223, + "lastReturnedLine": 241, + "sourceBytes": 526, + "partial": false + }, + { + "file": "mux.go", + "startLine": 242, + "lastReturnedLine": 267, + "sourceBytes": 880, + "partial": false + }, + { + "file": "mux.go", + "startLine": 268, + "lastReturnedLine": 277, + "sourceBytes": 253, + "partial": false + }, + { + "file": "mux.go", + "startLine": 278, + "lastReturnedLine": 293, + "sourceBytes": 605, + "partial": true + } + ], + "omittedGroups": 12, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "chi", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 476, + "expected": "\tmethod, ok := methodMap[rctx.RouteMethod]", + "returned": null + }, + { + "line": 477, + "expected": "\tif !ok {", + "returned": null + }, + { + "line": 478, + "expected": "\t\tmx.MethodNotAllowedHandler().ServeHTTP(w, r)", + "returned": null + }, + { + "line": 479, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 480, + "expected": "\t}", + "returned": null + }, + { + "line": 481, + "expected": "", + "returned": null + }, + { + "line": 482, + "expected": "\t// Find the route", + "returned": null + }, + { + "line": 483, + "expected": "\tif _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {", + "returned": null + }, + { + "line": 484, + "expected": "\t\t// Set http.Request path values from our request context", + "returned": null + }, + { + "line": 485, + "expected": "\t\tfor i, key := range rctx.URLParams.Keys {", + "returned": null + }, + { + "line": 486, + "expected": "\t\t\tvalue := rctx.URLParams.Values[i]", + "returned": null + }, + { + "line": 487, + "expected": "\t\t\tr.SetPathValue(key, value)", + "returned": null + }, + { + "line": 488, + "expected": "\t\t}", + "returned": null + }, + { + "line": 489, + "expected": "\t\tr.Pattern = rctx.RoutePattern()", + "returned": null + }, + { + "line": 490, + "expected": "", + "returned": null + }, + { + "line": 491, + "expected": "\t\th.ServeHTTP(w, r)", + "returned": null + }, + { + "line": 492, + "expected": "\t\treturn", + "returned": null + }, + { + "line": 493, + "expected": "\t}", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 2189, + "neighborWireBytes": 2319, + "returnedMembershipRows": 33, + "windows": [ + { + "file": "mux.go", + "startLine": 63, + "lastReturnedLine": 99, + "sourceBytes": 1373, + "partial": false + }, + { + "file": "mux.go", + "startLine": 100, + "lastReturnedLine": 108, + "sourceBytes": 333, + "partial": false + }, + { + "file": "mux.go", + "startLine": 109, + "lastReturnedLine": 120, + "sourceBytes": 386, + "partial": false + }, + { + "file": "mux.go", + "startLine": 121, + "lastReturnedLine": 126, + "sourceBytes": 217, + "partial": false + }, + { + "file": "mux.go", + "startLine": 127, + "lastReturnedLine": 136, + "sourceBytes": 356, + "partial": false + }, + { + "file": "mux.go", + "startLine": 137, + "lastReturnedLine": 142, + "sourceBytes": 241, + "partial": false + }, + { + "file": "mux.go", + "startLine": 143, + "lastReturnedLine": 148, + "sourceBytes": 230, + "partial": false + }, + { + "file": "mux.go", + "startLine": 149, + "lastReturnedLine": 154, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 155, + "lastReturnedLine": 160, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 161, + "lastReturnedLine": 166, + "sourceBytes": 227, + "partial": false + }, + { + "file": "mux.go", + "startLine": 167, + "lastReturnedLine": 172, + "sourceBytes": 228, + "partial": false + }, + { + "file": "mux.go", + "startLine": 173, + "lastReturnedLine": 178, + "sourceBytes": 222, + "partial": false + }, + { + "file": "mux.go", + "startLine": 179, + "lastReturnedLine": 184, + "sourceBytes": 218, + "partial": false + }, + { + "file": "mux.go", + "startLine": 185, + "lastReturnedLine": 190, + "sourceBytes": 220, + "partial": false + }, + { + "file": "mux.go", + "startLine": 191, + "lastReturnedLine": 196, + "sourceBytes": 224, + "partial": false + }, + { + "file": "mux.go", + "startLine": 197, + "lastReturnedLine": 202, + "sourceBytes": 242, + "partial": false + }, + { + "file": "mux.go", + "startLine": 203, + "lastReturnedLine": 222, + "sourceBytes": 579, + "partial": false + }, + { + "file": "mux.go", + "startLine": 223, + "lastReturnedLine": 241, + "sourceBytes": 526, + "partial": false + }, + { + "file": "mux.go", + "startLine": 242, + "lastReturnedLine": 267, + "sourceBytes": 880, + "partial": false + }, + { + "file": "mux.go", + "startLine": 268, + "lastReturnedLine": 277, + "sourceBytes": 253, + "partial": false + }, + { + "file": "mux.go", + "startLine": 278, + "lastReturnedLine": 293, + "sourceBytes": 605, + "partial": true + } + ], + "omittedGroups": 12, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "click", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 455, + "expected": "class _AtomicFile:", + "returned": null + } + ], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Only the class header is absent; the exact owner identity is verified independently. All initialization, @property and getter code is returned. Same semantic allowance as the prior registered window control." + }, + { + "fact": "click-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "click-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "click-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + } + ], + "sourceBytes": 3673, + "stdoutBytes": 0, + "neighborTextBytes": 2092, + "neighborWireBytes": 23086, + "returnedMembershipRows": 7, + "windows": [ + { + "file": "src/click/_compat.py", + "startLine": 456, + "lastReturnedLine": 462, + "sourceBytes": 236, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 463, + "lastReturnedLine": 465, + "sourceBytes": 63, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 466, + "lastReturnedLine": 472, + "sourceBytes": 206, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 473, + "lastReturnedLine": 475, + "sourceBytes": 86, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 476, + "lastReturnedLine": 478, + "sourceBytes": 61, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 479, + "lastReturnedLine": 486, + "sourceBytes": 217, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 487, + "lastReturnedLine": 590, + "sourceBytes": 2804, + "partial": false + } + ], + "omittedGroups": 0, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "click", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 455, + "expected": "class _AtomicFile:", + "returned": null + } + ], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Only the class header is absent; the exact owner identity is verified independently. All initialization, @property and getter code is returned. Same semantic allowance as the prior registered window control." + }, + { + "fact": "click-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "click-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "click-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + } + ], + "sourceBytes": 3673, + "stdoutBytes": 0, + "neighborTextBytes": 636, + "neighborWireBytes": 735, + "returnedMembershipRows": 7, + "windows": [ + { + "file": "src/click/_compat.py", + "startLine": 456, + "lastReturnedLine": 462, + "sourceBytes": 236, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 463, + "lastReturnedLine": 465, + "sourceBytes": 63, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 466, + "lastReturnedLine": 472, + "sourceBytes": 206, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 473, + "lastReturnedLine": 475, + "sourceBytes": 86, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 476, + "lastReturnedLine": 478, + "sourceBytes": 61, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 479, + "lastReturnedLine": 486, + "sourceBytes": 217, + "partial": false + }, + { + "file": "src/click/_compat.py", + "startLine": 487, + "lastReturnedLine": 590, + "sourceBytes": 2804, + "partial": false + } + ], + "omittedGroups": 0, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "jsoup", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 206, + "expected": " Range.AttributeRange range = sourceAttrs.sourceRange(key);", + "returned": " Range.AttributeRange r" + }, + { + "line": 207, + "expected": " destAttrs.put(key, value);", + "returned": null + }, + { + "line": 208, + "expected": " NodeInternals.attributeRange(destAttrs, key, range);", + "returned": null + }, + { + "line": 209, + "expected": " } else", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 7014, + "neighborWireBytes": 66437, + "returnedMembershipRows": 9, + "windows": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 44, + "lastReturnedLine": 49, + "sourceBytes": 180, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 50, + "lastReturnedLine": 61, + "sourceBytes": 541, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "lastReturnedLine": 93, + "sourceBytes": 1549, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 94, + "lastReturnedLine": 123, + "sourceBytes": 1625, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 124, + "lastReturnedLine": 137, + "sourceBytes": 748, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 138, + "lastReturnedLine": 181, + "sourceBytes": 2032, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "lastReturnedLine": 187, + "sourceBytes": 233, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 188, + "lastReturnedLine": 206, + "sourceBytes": 1092, + "partial": true + } + ], + "omittedGroups": 1, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "jsoup", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1034, + "neighborWireBytes": 1135, + "returnedMembershipRows": 8, + "windows": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 50, + "lastReturnedLine": 61, + "sourceBytes": 541, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 62, + "lastReturnedLine": 93, + "sourceBytes": 1549, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 94, + "lastReturnedLine": 123, + "sourceBytes": 1625, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 124, + "lastReturnedLine": 137, + "sourceBytes": 748, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 138, + "lastReturnedLine": 181, + "sourceBytes": 2032, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 182, + "lastReturnedLine": 187, + "sourceBytes": 233, + "partial": false + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 188, + "lastReturnedLine": 210, + "sourceBytes": 1272, + "partial": true + } + ], + "omittedGroups": 1, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "explicitNativeFacts": 0 + }, + { + "repository": "redux", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "redux-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "redux-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 344, + "expected": " function observable() {", + "returned": null + }, + { + "line": 345, + "expected": " const outerSubscribe = subscribe", + "returned": null + }, + { + "line": 346, + "expected": " return {", + "returned": null + }, + { + "line": 347, + "expected": " /**", + "returned": null + }, + { + "line": 348, + "expected": " * The minimal observable subscription method.", + "returned": null + }, + { + "line": 349, + "expected": " * @param observer Any object that can be used as an observer.", + "returned": null + }, + { + "line": 350, + "expected": " * The observer object should have a `next` method.", + "returned": null + }, + { + "line": 351, + "expected": " * @returns An object with an `unsubscribe` method that can", + "returned": null + }, + { + "line": 352, + "expected": " * be used to unsubscribe the observable from the store, and prevent further", + "returned": null + }, + { + "line": 353, + "expected": " * emission of values from the observable.", + "returned": null + }, + { + "line": 354, + "expected": " */", + "returned": null + }, + { + "line": 355, + "expected": " subscribe(observer: unknown) {", + "returned": null + }, + { + "line": 356, + "expected": " if (typeof observer !== 'object' || observer === null) {", + "returned": null + }, + { + "line": 357, + "expected": " throw new TypeError(", + "returned": null + }, + { + "line": 358, + "expected": " `Expected the observer to be an object. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 359, + "expected": " observer", + "returned": null + }, + { + "line": 360, + "expected": " )}'`", + "returned": null + }, + { + "line": 361, + "expected": " )", + "returned": null + }, + { + "line": 362, + "expected": " }", + "returned": null + }, + { + "line": 363, + "expected": "", + "returned": null + }, + { + "line": 364, + "expected": " function observeState() {", + "returned": null + }, + { + "line": 365, + "expected": " const observerAsObserver = observer as Observer", + "returned": null + }, + { + "line": 366, + "expected": " if (observerAsObserver.next) {", + "returned": null + }, + { + "line": 367, + "expected": " observerAsObserver.next(getState())", + "returned": null + }, + { + "line": 368, + "expected": " }", + "returned": null + }, + { + "line": 369, + "expected": " }", + "returned": null + }, + { + "line": 370, + "expected": "", + "returned": null + }, + { + "line": 371, + "expected": " observeState()", + "returned": null + }, + { + "line": 372, + "expected": " const unsubscribe = outerSubscribe(observeState)", + "returned": null + }, + { + "line": 373, + "expected": " return { unsubscribe }", + "returned": null + }, + { + "line": 374, + "expected": " },", + "returned": null + }, + { + "line": 375, + "expected": "", + "returned": null + }, + { + "line": 376, + "expected": " [$$observable]() {", + "returned": null + }, + { + "line": 377, + "expected": " return this", + "returned": null + }, + { + "line": 378, + "expected": " }", + "returned": null + }, + { + "line": 379, + "expected": " }", + "returned": null + }, + { + "line": 380, + "expected": " }", + "returned": null + }, + { + "line": 381, + "expected": "", + "returned": null + }, + { + "line": 382, + "expected": " // When a store is created, an \"INIT\" action is dispatched so that every", + "returned": null + }, + { + "line": 383, + "expected": " // reducer returns their initial state. This effectively populates", + "returned": null + }, + { + "line": 384, + "expected": " // the initial state tree.", + "returned": null + }, + { + "line": 385, + "expected": " dispatch({ type: ActionTypes.INIT } as A)", + "returned": null + }, + { + "line": 386, + "expected": "", + "returned": null + }, + { + "line": 387, + "expected": " const store = {", + "returned": null + }, + { + "line": 388, + "expected": " dispatch: dispatch as Dispatch,", + "returned": null + }, + { + "line": 389, + "expected": " subscribe,", + "returned": null + }, + { + "line": 390, + "expected": " getState,", + "returned": null + }, + { + "line": 391, + "expected": " replaceReducer,", + "returned": null + }, + { + "line": 392, + "expected": " [$$observable]: observable", + "returned": null + }, + { + "line": 393, + "expected": " } as unknown as Store & Ext", + "returned": null + }, + { + "line": 394, + "expected": " return store", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 6564, + "neighborWireBytes": 91587, + "returnedMembershipRows": 23, + "windows": [ + { + "file": "src/createStore.ts", + "startLine": 87, + "lastReturnedLine": 87, + "sourceBytes": 5, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 88, + "lastReturnedLine": 88, + "sourceBytes": 20, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 89, + "lastReturnedLine": 89, + "sourceBytes": 23, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 90, + "lastReturnedLine": 90, + "sourceBytes": 28, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 91, + "lastReturnedLine": 94, + "sourceBytes": 144, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 95, + "lastReturnedLine": 135, + "sourceBytes": 1298, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 136, + "lastReturnedLine": 136, + "sourceBytes": 31, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 137, + "lastReturnedLine": 139, + "sourceBytes": 108, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 140, + "lastReturnedLine": 140, + "sourceBytes": 73, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 141, + "lastReturnedLine": 141, + "sourceBytes": 39, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 142, + "lastReturnedLine": 142, + "sourceBytes": 28, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 143, + "lastReturnedLine": 151, + "sourceBytes": 278, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 152, + "lastReturnedLine": 165, + "sourceBytes": 358, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 166, + "lastReturnedLine": 200, + "sourceBytes": 1555, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 201, + "lastReturnedLine": 269, + "sourceBytes": 2608, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 270, + "lastReturnedLine": 312, + "sourceBytes": 1404, + "partial": true + } + ], + "omittedGroups": 7, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "explicitNativeFacts": 0 + }, + { + "repository": "redux", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 121, + "expected": " if (typeof enhancer !== 'undefined') {", + "returned": null + }, + { + "line": 122, + "expected": " if (typeof enhancer !== 'function') {", + "returned": null + }, + { + "line": 123, + "expected": " throw new Error(", + "returned": null + }, + { + "line": 124, + "expected": " `Expected the enhancer to be a function. Instead, received: '${kindOf(", + "returned": null + }, + { + "line": 125, + "expected": " enhancer", + "returned": null + }, + { + "line": 126, + "expected": " )}'`", + "returned": null + }, + { + "line": 127, + "expected": " )", + "returned": null + }, + { + "line": 128, + "expected": " }", + "returned": null + }, + { + "line": 129, + "expected": "", + "returned": null + }, + { + "line": 130, + "expected": " return enhancer(createStore)(", + "returned": null + }, + { + "line": 131, + "expected": " reducer,", + "returned": null + }, + { + "line": 132, + "expected": " preloadedState as PreloadedState | undefined", + "returned": null + }, + { + "line": 133, + "expected": " )", + "returned": null + }, + { + "line": 134, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "redux-3", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 365, + "expected": " const observerAsObserver = observer as Observer", + "returned": " const observerAsObserver = observer a" + }, + { + "line": 366, + "expected": " if (observerAsObserver.next) {", + "returned": null + }, + { + "line": 367, + "expected": " observerAsObserver.next(getState())", + "returned": null + }, + { + "line": 368, + "expected": " }", + "returned": null + }, + { + "line": 369, + "expected": " }", + "returned": null + }, + { + "line": 370, + "expected": "", + "returned": null + }, + { + "line": 371, + "expected": " observeState()", + "returned": null + }, + { + "line": 372, + "expected": " const unsubscribe = outerSubscribe(observeState)", + "returned": null + }, + { + "line": 373, + "expected": " return { unsubscribe }", + "returned": null + }, + { + "line": 374, + "expected": " },", + "returned": null + }, + { + "line": 375, + "expected": "", + "returned": null + }, + { + "line": 376, + "expected": " [$$observable]() {", + "returned": null + }, + { + "line": 377, + "expected": " return this", + "returned": null + }, + { + "line": 378, + "expected": " }", + "returned": null + }, + { + "line": 379, + "expected": " }", + "returned": null + }, + { + "line": 380, + "expected": " }", + "returned": null + }, + { + "line": 381, + "expected": "", + "returned": null + }, + { + "line": 382, + "expected": " // When a store is created, an \"INIT\" action is dispatched so that every", + "returned": null + }, + { + "line": 383, + "expected": " // reducer returns their initial state. This effectively populates", + "returned": null + }, + { + "line": 384, + "expected": " // the initial state tree.", + "returned": null + }, + { + "line": 385, + "expected": " dispatch({ type: ActionTypes.INIT } as A)", + "returned": null + }, + { + "line": 386, + "expected": "", + "returned": null + }, + { + "line": 387, + "expected": " const store = {", + "returned": null + }, + { + "line": 388, + "expected": " dispatch: dispatch as Dispatch,", + "returned": null + }, + { + "line": 389, + "expected": " subscribe,", + "returned": null + }, + { + "line": 390, + "expected": " getState,", + "returned": null + }, + { + "line": 391, + "expected": " replaceReducer,", + "returned": null + }, + { + "line": 392, + "expected": " [$$observable]: observable", + "returned": null + }, + { + "line": 393, + "expected": " } as unknown as Store & Ext", + "returned": null + }, + { + "line": 394, + "expected": " return store", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1785, + "neighborWireBytes": 1897, + "returnedMembershipRows": 2, + "windows": [ + { + "file": "src/createStore.ts", + "startLine": 152, + "lastReturnedLine": 269, + "sourceBytes": 4521, + "partial": false + }, + { + "file": "src/createStore.ts", + "startLine": 270, + "lastReturnedLine": 365, + "sourceBytes": 3479, + "partial": true + } + ], + "omittedGroups": 0, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "explicitNativeFacts": 0 + }, + { + "repository": "walkdir", + "tool": "compass", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 901, + "expected": " fn push(&mut self, dent: &DirEntry) -> Result<()> {", + "returned": null + }, + { + "line": 902, + "expected": " // Make room for another open file descriptor if we've hit the max.", + "returned": null + }, + { + "line": 903, + "expected": " let free =", + "returned": null + }, + { + "line": 904, + "expected": " self.stack_list.len().checked_sub(self.oldest_opened).unwrap();", + "returned": null + }, + { + "line": 905, + "expected": " if free == self.opts.max_open {", + "returned": null + }, + { + "line": 906, + "expected": " self.stack_list[self.oldest_opened].close();", + "returned": null + }, + { + "line": 907, + "expected": " }", + "returned": null + }, + { + "line": 908, + "expected": " // Open a handle to reading the directory's entries.", + "returned": null + }, + { + "line": 909, + "expected": " let rd = fs::read_dir(dent.path()).map_err(|err| {", + "returned": null + }, + { + "line": 910, + "expected": " Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))", + "returned": null + }, + { + "line": 911, + "expected": " });", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 961, + "expected": " fn follow(&self, mut dent: DirEntry) -> Result {", + "returned": null + }, + { + "line": 962, + "expected": " dent =", + "returned": null + }, + { + "line": 963, + "expected": " DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;", + "returned": null + }, + { + "line": 964, + "expected": " // The only way a symlink can cause a loop is if it points", + "returned": null + }, + { + "line": 965, + "expected": " // to a directory. Otherwise, it always points to a leaf", + "returned": null + }, + { + "line": 966, + "expected": " // and we can omit any loop checks.", + "returned": null + }, + { + "line": 967, + "expected": " if dent.is_dir() {", + "returned": null + }, + { + "line": 968, + "expected": " self.check_loop(dent.path())?;", + "returned": null + }, + { + "line": 969, + "expected": " }", + "returned": null + }, + { + "line": 970, + "expected": " Ok(dent)", + "returned": null + }, + { + "line": 971, + "expected": " }", + "returned": null + }, + { + "line": 972, + "expected": "", + "returned": null + }, + { + "line": 973, + "expected": " fn check_loop>(&self, child: P) -> Result<()> {", + "returned": null + }, + { + "line": 974, + "expected": " let hchild = Handle::from_path(&child)", + "returned": null + }, + { + "line": 975, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 976, + "expected": " for ancestor in self.stack_path.iter().rev() {", + "returned": null + }, + { + "line": 977, + "expected": " let is_same = ancestor", + "returned": null + }, + { + "line": 978, + "expected": " .is_same(&hchild)", + "returned": null + }, + { + "line": 979, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 980, + "expected": " if is_same {", + "returned": null + }, + { + "line": 981, + "expected": " return Err(Error::from_loop(", + "returned": null + }, + { + "line": 982, + "expected": " self.depth,", + "returned": null + }, + { + "line": 983, + "expected": " &ancestor.path,", + "returned": null + }, + { + "line": 984, + "expected": " child.as_ref(),", + "returned": null + }, + { + "line": 985, + "expected": " ));", + "returned": null + }, + { + "line": 986, + "expected": " }", + "returned": null + }, + { + "line": 987, + "expected": " }", + "returned": null + }, + { + "line": 988, + "expected": " Ok(())", + "returned": null + }, + { + "line": 989, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 873, + "expected": " }", + "returned": null + }, + { + "line": 874, + "expected": " if is_normal_dir && self.opts.contents_first {", + "returned": null + }, + { + "line": 875, + "expected": " self.deferred_dirs.push(dent);", + "returned": null + }, + { + "line": 876, + "expected": " None", + "returned": null + }, + { + "line": 877, + "expected": " } else if self.skippable() {", + "returned": null + }, + { + "line": 878, + "expected": " None", + "returned": null + }, + { + "line": 879, + "expected": " } else {", + "returned": null + }, + { + "line": 880, + "expected": " Some(Ok(dent))", + "returned": null + }, + { + "line": 881, + "expected": " }", + "returned": null + }, + { + "line": 882, + "expected": " }", + "returned": null + }, + { + "line": 883, + "expected": "", + "returned": null + }, + { + "line": 884, + "expected": " fn get_deferred_dir(&mut self) -> Option {", + "returned": null + }, + { + "line": 885, + "expected": " if self.opts.contents_first {", + "returned": null + }, + { + "line": 886, + "expected": " if self.depth < self.deferred_dirs.len() {", + "returned": null + }, + { + "line": 887, + "expected": " // Unwrap is safe here because we've guaranteed that", + "returned": null + }, + { + "line": 888, + "expected": " // `self.deferred_dirs.len()` can never be less than 1", + "returned": null + }, + { + "line": 889, + "expected": " let deferred: DirEntry = self", + "returned": null + }, + { + "line": 890, + "expected": " .deferred_dirs", + "returned": null + }, + { + "line": 891, + "expected": " .pop()", + "returned": null + }, + { + "line": 892, + "expected": " .expect(\"BUG: deferred_dirs should be non-empty\");", + "returned": null + }, + { + "line": 893, + "expected": " if !self.skippable() {", + "returned": null + }, + { + "line": 894, + "expected": " return Some(deferred);", + "returned": null + }, + { + "line": 895, + "expected": " }", + "returned": null + }, + { + "line": 896, + "expected": " }", + "returned": null + }, + { + "line": 897, + "expected": " }", + "returned": null + }, + { + "line": 898, + "expected": " None", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 4237, + "neighborWireBytes": 44444, + "returnedMembershipRows": 20, + "windows": [ + { + "file": "src/lib.rs", + "startLine": 568, + "lastReturnedLine": 572, + "sourceBytes": 167, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 573, + "lastReturnedLine": 578, + "sourceBytes": 348, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 579, + "lastReturnedLine": 585, + "sourceBytes": 253, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 586, + "lastReturnedLine": 590, + "sourceBytes": 283, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 591, + "lastReturnedLine": 593, + "sourceBytes": 135, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 594, + "lastReturnedLine": 597, + "sourceBytes": 207, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 598, + "lastReturnedLine": 604, + "sourceBytes": 318, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 605, + "lastReturnedLine": 679, + "sourceBytes": 2788, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 680, + "lastReturnedLine": 686, + "sourceBytes": 273, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 687, + "lastReturnedLine": 776, + "sourceBytes": 3228, + "partial": true + } + ], + "omittedGroups": 10, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "explicitNativeFacts": 0 + }, + { + "repository": "walkdir", + "tool": "graphify", + "arm": "neighbor-window-control", + "resolverStatus": "resolved", + "judgments": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 902, + "expected": " // Make room for another open file descriptor if we've hit the max.", + "returned": " // Make room for another open file descriptor if we" + }, + { + "line": 903, + "expected": " let free =", + "returned": null + }, + { + "line": 904, + "expected": " self.stack_list.len().checked_sub(self.oldest_opened).unwrap();", + "returned": null + }, + { + "line": 905, + "expected": " if free == self.opts.max_open {", + "returned": null + }, + { + "line": 906, + "expected": " self.stack_list[self.oldest_opened].close();", + "returned": null + }, + { + "line": 907, + "expected": " }", + "returned": null + }, + { + "line": 908, + "expected": " // Open a handle to reading the directory's entries.", + "returned": null + }, + { + "line": 909, + "expected": " let rd = fs::read_dir(dent.path()).map_err(|err| {", + "returned": null + }, + { + "line": 910, + "expected": " Some(Error::from_path(self.depth, dent.path().to_path_buf(), err))", + "returned": null + }, + { + "line": 911, + "expected": " });", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "missingAfterIndentNormalization": [ + { + "line": 961, + "expected": " fn follow(&self, mut dent: DirEntry) -> Result {", + "returned": null + }, + { + "line": 962, + "expected": " dent =", + "returned": null + }, + { + "line": 963, + "expected": " DirEntry::from_path(self.depth, dent.path().to_path_buf(), true)?;", + "returned": null + }, + { + "line": 964, + "expected": " // The only way a symlink can cause a loop is if it points", + "returned": null + }, + { + "line": 965, + "expected": " // to a directory. Otherwise, it always points to a leaf", + "returned": null + }, + { + "line": 966, + "expected": " // and we can omit any loop checks.", + "returned": null + }, + { + "line": 967, + "expected": " if dent.is_dir() {", + "returned": null + }, + { + "line": 968, + "expected": " self.check_loop(dent.path())?;", + "returned": null + }, + { + "line": 969, + "expected": " }", + "returned": null + }, + { + "line": 970, + "expected": " Ok(dent)", + "returned": null + }, + { + "line": 971, + "expected": " }", + "returned": null + }, + { + "line": 972, + "expected": "", + "returned": null + }, + { + "line": 973, + "expected": " fn check_loop>(&self, child: P) -> Result<()> {", + "returned": null + }, + { + "line": 974, + "expected": " let hchild = Handle::from_path(&child)", + "returned": null + }, + { + "line": 975, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 976, + "expected": " for ancestor in self.stack_path.iter().rev() {", + "returned": null + }, + { + "line": 977, + "expected": " let is_same = ancestor", + "returned": null + }, + { + "line": 978, + "expected": " .is_same(&hchild)", + "returned": null + }, + { + "line": 979, + "expected": " .map_err(|err| Error::from_io(self.depth, err))?;", + "returned": null + }, + { + "line": 980, + "expected": " if is_same {", + "returned": null + }, + { + "line": 981, + "expected": " return Err(Error::from_loop(", + "returned": null + }, + { + "line": 982, + "expected": " self.depth,", + "returned": null + }, + { + "line": 983, + "expected": " &ancestor.path,", + "returned": null + }, + { + "line": 984, + "expected": " child.as_ref(),", + "returned": null + }, + { + "line": 985, + "expected": " ));", + "returned": null + }, + { + "line": 986, + "expected": " }", + "returned": null + }, + { + "line": 987, + "expected": " }", + "returned": null + }, + { + "line": 988, + "expected": " Ok(())", + "returned": null + }, + { + "line": 989, + "expected": " }", + "returned": null + } + ], + "sufficientSourceEvidence": false, + "explicitNativeAssertion": false, + "reason": "Required implementation code is absent or partial; no partial-fact credit." + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": true, + "missingAfterIndentNormalization": [], + "sufficientSourceEvidence": true, + "explicitNativeAssertion": false, + "reason": "Every literal source witness is returned." + } + ], + "sourceBytes": 8000, + "stdoutBytes": 0, + "neighborTextBytes": 1336, + "neighborWireBytes": 1448, + "returnedMembershipRows": 11, + "windows": [ + { + "file": "src/lib.rs", + "startLine": 687, + "lastReturnedLine": 780, + "sourceBytes": 3391, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 781, + "lastReturnedLine": 832, + "sourceBytes": 1974, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 833, + "lastReturnedLine": 839, + "sourceBytes": 175, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 840, + "lastReturnedLine": 883, + "sourceBytes": 1732, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 884, + "lastReturnedLine": 900, + "sourceBytes": 613, + "partial": false + }, + { + "file": "src/lib.rs", + "startLine": 901, + "lastReturnedLine": 902, + "sourceBytes": 115, + "partial": true + } + ], + "omittedGroups": 5, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "explicitNativeFacts": 0 + } + ], + "identityChecks": { + "defaultResolverResponsesUnchanged": 10, + "unchangedPriorNeighborAndWindowControls": [ + { + "repository": "chi", + "tool": "compass" + }, + { + "repository": "chi", + "tool": "graphify" + }, + { + "repository": "click", + "tool": "compass" + }, + { + "repository": "click", + "tool": "graphify" + }, + { + "repository": "jsoup", + "tool": "compass" + }, + { + "repository": "jsoup", + "tool": "graphify" + }, + { + "repository": "redux", + "tool": "graphify" + }, + { + "repository": "walkdir", + "tool": "compass" + }, + { + "repository": "walkdir", + "tool": "graphify" + } + ], + "newReduxEndpoint": { + "id": "sha256:ceae4b39dd8902b6888096a1827ee67422d04d3c19d527d674ec740c3e903785", + "kind": "function", + "file": "src/createStore.ts", + "line": 86, + "sourceDeclaration": "export function createStore<" + }, + "negativeControls": "Without the explicit function kind, exact Redux lookup returns both the declaration and its coincident export record. A nonexistent declaration line returns no match with complete execution. No binding is silently discarded.", + "sourceKinds": "Pinned source declaration lines were checked for Mux struct, _AtomicFile class, Cleaner class, createStore function and IntoIter struct. Graphify get_node does not accept the kind filter; its returned identity is checked against the same supplied constraints after capture.", + "publicCallsVerified": 32, + "sourceWindowPolicyFunctionHashes": { + "anchors": "b49ff9fc521d4eecfae2987fd3706b461c09a0dbb5161693afa67aa32ab3424a", + "windows": "b39fbb5cf512a35b2ea4385fcc857f29118506057c728a529b869ae4b70abeb5" + } + }, + "comparison": { + "priorUnscopedWindowControl": { + "compass": 11, + "graphify": 15, + "facts": 20 + }, + "newConstrainedWindowControl": { + "compass": 14, + "graphify": 15, + "facts": 20 + }, + "delta": "Three newly supported Redux facts: enhancer behavior, dispatch state/reentrancy, and listener snapshot/unsubscribe semantics. Earlier parameter anchors also expose the enhancer body. Observable/store API code remains beyond the source budget.", + "remainingGap": "Compass trails Graphify by one total fact: Graphify gains jsoup attribute-copy and WalkDir deferred-directory evidence; Compass gains Redux enhancer evidence. Both miss other facts.", + "noAggregation": "Explicit file, declaration line and kind are supplied task constraints in this new arm. Do not retroactively replace the old unresolved result or combine this score with native source-member/declaration arms." + }, + "validation": { + "baseCommit": "2ef40e95ca45972f92696cc5d427c8e9808476dd", + "sourceSha256": { + "crates/compass-output/src/agent_query.rs": "8fc6f67d56f17c3298a4752a0ab7b7f5fca2eea3ca4868536c2f13f5d2edb300", + "crates/compass-output/tests/agent_query.rs": "19ddf5eb5f5fc4c06257b3219317789c77bb9d042aed2e986845be93e23d2562", + "crates/compass-query/src/code_query.rs": "9af2f720b1b5d6962482073d29547c049ec8480500049c9c74ae565a85921800", + "crates/compass-query/src/lib.rs": "982a3bd5c8213556d04454b8bd08f2c7a2c61f516ea0639bc3f92b4279ee08e6", + "crates/compass-query/tests/code_search.rs": "f14342daacc57ada5d496f4b4baf565bff8c5f232a93324d5b0782accf1a2796", + "crates/compass-cli/src/code_query_commands.rs": "a0820b81ea78212cdc0dca0d88f66c3ee8be89fe7afd168526323194b8a8c0d6", + "crates/compass-cli/src/help.rs": "a4ba97efbbaa3d6d8a6a4c2883f9d07615ba7324602fd56040ded958cacd3edc", + "crates/compass-cli/tests/code_query_cli.rs": "1690034e88dbdcbd024f755d360f1eebba17d3db87dd688f62b69077ec544fb8", + "crates/compass-mcp/src/code_query.rs": "24e676846d0d8a6a158a0417fe3248bfa76dc1fde7f166deb4bfd2373f011d6d", + "crates/compass-mcp/src/lib.rs": "837fcc126e236be64611bf58ac08b82fd683fd7d667aa5b5ef1df2f98614f6bb", + "crates/compass-mcp/tests/code_query_tools.rs": "74f1a9d4e14468761e0b08395e37e36ff323e00bc35bfb50f659211c5fff259b" + }, + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 3.81 + }, + { + "name": "query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--test", + "code_search", + "--test", + "store_engine", + "--test", + "export_binding_resolution", + "--locked" + ], + "exitCode": 0, + "seconds": 18.39 + }, + { + "name": "output-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-output", + "--test", + "agent_query", + "--locked" + ], + "exitCode": 0, + "seconds": 0.65 + }, + { + "name": "cli-query-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "code_query_cli", + "--locked" + ], + "exitCode": 0, + "seconds": 2.85 + }, + { + "name": "mcp-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-mcp", + "--locked" + ], + "exitCode": 0, + "seconds": 26.07 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 43.66 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 98.44 + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 14.83 + }, + { + "name": "product-boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.04 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "--locked", + "-p", + "compass-cli", + "--bin", + "compass" + ], + "exitCode": 0, + "seconds": 0.39 + } + ] + }, + "testCounts": { + "product-tests-final.log": { + "passed": 9, + "failed": 0, + "ignored": 0 + }, + "workspace-tests-final.log": { + "passed": 1106, + "failed": 0, + "ignored": 2 + }, + "mcp-tests-final.log": { + "passed": 61, + "failed": 0, + "ignored": 0 + }, + "cli-query-tests-final.log": { + "passed": 41, + "failed": 0, + "ignored": 0 + }, + "output-tests-final.log": { + "passed": 20, + "failed": 0, + "ignored": 0 + }, + "query-tests-final.log": { + "passed": 37, + "failed": 0, + "ignored": 0 + } + }, + "validationScope": "Formatting, targeted query/backend/export-binding suites, output contract suite, CLI suite, full MCP suite, workspace Clippy and lib/bin tests, product tests, product boundary, and CLI build passed. JS/viewer and extraction/publication qualification not rerun: these surfaces are unchanged. Shared Python benchmark modules unchanged; external collector/verifier completed.", + "reproductions": [ + "Attempt 01 failed to compile a new test fixture using edges instead of links; corrected.", + "Attempt 02 rejected the new fixture because its other.rs file record was missing; corrected.", + "Attempt 03 exposed a new exact-mode response bug: node bounds left excess hits. Candidate truncation now happens before projecting both nodes and hits.", + "Attempt 04 CLI test incorrectly expected raw diagnostic prose in the compact text formatter; changed assertion to its documented bounded_truncation status.", + "Attempts 05/06 exposed an Agent View failure on empty truncated search: it attempted no_match without evidence of absence. It now preserves unknown match/partial execution and passes regression coverage. The legacy MCP error assertion also needed its documented Error prefix.", + "Attempt 07 focused output/query/CLI/MCP checks pass; attempt 08 records the final unchanged-source validation and repository capture." + ], + "artifacts": { + "exact-symbol-01/focused.py": "ae34625de3762e955494d54d97933bc1caf7f4a0f6b7af64212f2606c4591d70", + "exact-symbol-01/query.log": "cf3347ab792464520b0c6c3860af2195788b8ca216be1ddaa3a0904079396602", + "exact-symbol-02/focused.py": "ae34625de3762e955494d54d97933bc1caf7f4a0f6b7af64212f2606c4591d70", + "exact-symbol-02/query.log": "4a791e31e736be06d8f5b4456202935eab61f85ccdb7be3f8b4582306a7ff3a2", + "exact-symbol-03/focused.py": "ae34625de3762e955494d54d97933bc1caf7f4a0f6b7af64212f2606c4591d70", + "exact-symbol-03/query.log": "70df33567d79460f86fb38428f9dd1501022d4333a643d845b7564c73138cf4c", + "exact-symbol-04/cli.log": "17085d17fd7b5b8ec2fadf5e028ad6ceb223c885490e831b296dc270038a1f92", + "exact-symbol-04/focused.py": "ae34625de3762e955494d54d97933bc1caf7f4a0f6b7af64212f2606c4591d70", + "exact-symbol-04/query.log": "9b9182b7f2b77f876380fa4b1ff85489e4fb0179c307f537603fc58980143b79", + "exact-symbol-05/cli.log": "580a27c6fdb11e8732eae5c5a7aa6031a8133bda72e290289d5aa847d78b7a4d", + "exact-symbol-05/collect.py": "44fc18ff82318dd82b3de106d4a8c8bc55f2d882eb8badf5e1699c43493fb8bd", + "exact-symbol-05/focused.py": "ae34625de3762e955494d54d97933bc1caf7f4a0f6b7af64212f2606c4591d70", + "exact-symbol-05/mcp.log": "93b3fa40d7aeaddb6fe40eff1ffd607d5da5dee5c3240a148bd16496fff82f37", + "exact-symbol-05/query.log": "a9ea5a790ac1e192a5da9333a6e93a5461703aaf29ee6147d28502b919401d15", + "exact-symbol-05/validate.py": "f054b50fedf61cab3e5600318486c8dc82a0d09f96815b8ba65d69a124a42020", + "exact-symbol-05/verify_capture.py": "ee23ac7674d87dec92a01b3e8e244ac6957f6e3473dc718832c51d9394182168", + "exact-symbol-05/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "exact-symbol-06/focused.py": "1e9010ccd6a59666c9694c60c945ae653b189bb2c2b06019565387823ab76530", + "exact-symbol-06/mcp.log": "f9a4f08a714331df18a3503c2aba60833de8ff1de6d00b9e0759433b62433067", + "exact-symbol-07/cli.log": "6a2111eba9eaea4083ef90a0089c039aabdbcfed119e49d5688e7f6d6bd2f994", + "exact-symbol-07/focused.py": "06df8a5d7969a49bbcac09f3e71feac96d836305b8c2009b69ccbbbb31c16b54", + "exact-symbol-07/mcp.log": "eabd86c386033baa83387b6470948adb99e65b0e242d0ce4319ed877fb4ca0b2", + "exact-symbol-07/output.log": "f35fa7bc346afa0677067d4fb9b9cc443dbc586367074811026ef0f963b5f61a", + "exact-symbol-07/query.log": "b4861ddcd537e02b5c63dfccdeaaff3b621337373b7f447df0c13efd073c291a", + "exact-symbol-08/build-final.log": "9e016f5f155581ff3875df43030558b9deaf182b9be48c4c84b52b9bf783046b", + "exact-symbol-08/capture/capture.json": "4263f8d01ace8a5d7caa3595642115875bdd91c63e5b3a3d03bd12c3abd3c91a", + "exact-symbol-08/capture/collect.py": "44fc18ff82318dd82b3de106d4a8c8bc55f2d882eb8badf5e1699c43493fb8bd", + "exact-symbol-08/capture/community_identity.py": "5bc3d1c574211aa3180a2334fe6c758043f384651948b7077aba3ea879de4503", + "exact-symbol-08/capture/community_navigation.py": "808df1c7b15c8c2afc86ac1ed09dc4f339c98a77d7a7b14eb4866e3da1c8c0f8", + "exact-symbol-08/capture/community_tasks.py": "77f599ff7ee7eebfe4b590fee6c771ecfb3ed475d1944e37bcabc110634d3660", + "exact-symbol-08/capture/exact_symbol_development_registration.json": "78781c22e1357515e9cd4e1093ecabe0fc1263262fae3a4fee1b0bf02a8086f2", + "exact-symbol-08/capture/graphify-mcp-environment.json": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535", + "exact-symbol-08/capture/mcp_compare.py": "d23f093a1300fcf825457f917694097f0f8b8dc8ee8d671946032f5e4e89a5dc", + "exact-symbol-08/capture/mcp_transport.py": "daaad69c554f824a1df94f0ef93cb9d4c6e9b4f4b712d1611323d524e10d44ee", + "exact-symbol-08/capture/raw/chi/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/chi/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "exact-symbol-08/capture/raw/chi/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/chi/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/chi/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "exact-symbol-08/capture/raw/chi/compass/mcp/04.request.json": "b825c94e647e210dbf316cbbd7220d9e0fd22ffb58190d125770ee341c9eeb4c", + "exact-symbol-08/capture/raw/chi/compass/mcp/04.response.jsonl": "9592fe0f5e3501858b96e73f166bfb9abfa98fc0cfeaeff911ff5b9deb383871", + "exact-symbol-08/capture/raw/chi/compass/mcp/05.request.json": "e71b68a06dc19edf7714aa8da45ecc4f622709859ed76f8abde1a264eac6cab0", + "exact-symbol-08/capture/raw/chi/compass/mcp/05.response.jsonl": "34457c5e5dde851a97ba147dcf47065d3bf30d45af44898c0360d9568c79eb7a", + "exact-symbol-08/capture/raw/chi/compass/mcp/06.request.json": "d554a89dd893efe9555f85df9574d29a28a43a1e3281df8d114c81883d7a8412", + "exact-symbol-08/capture/raw/chi/compass/mcp/06.response.jsonl": "ffeb9c96e595a85c958c528bce6f050c7734aedcbbc771cb147daae1705bf713", + "exact-symbol-08/capture/raw/chi/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/chi/compass/windows/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "exact-symbol-08/capture/raw/chi/compass/windows/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "exact-symbol-08/capture/raw/chi/compass/windows/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "exact-symbol-08/capture/raw/chi/compass/windows/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "exact-symbol-08/capture/raw/chi/compass/windows/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "exact-symbol-08/capture/raw/chi/compass/windows/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "exact-symbol-08/capture/raw/chi/compass/windows/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "exact-symbol-08/capture/raw/chi/compass/windows/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "exact-symbol-08/capture/raw/chi/compass/windows/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "exact-symbol-08/capture/raw/chi/compass/windows/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "exact-symbol-08/capture/raw/chi/compass/windows/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "exact-symbol-08/capture/raw/chi/compass/windows/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "exact-symbol-08/capture/raw/chi/compass/windows/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "exact-symbol-08/capture/raw/chi/compass/windows/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "exact-symbol-08/capture/raw/chi/compass/windows/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "exact-symbol-08/capture/raw/chi/compass/windows/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "exact-symbol-08/capture/raw/chi/compass/windows/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "exact-symbol-08/capture/raw/chi/compass/windows/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "exact-symbol-08/capture/raw/chi/compass/windows/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "exact-symbol-08/capture/raw/chi/compass/windows/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "exact-symbol-08/capture/raw/chi/compass/windows/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "exact-symbol-08/capture/raw/chi/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/chi/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "exact-symbol-08/capture/raw/chi/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/chi/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/chi/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "exact-symbol-08/capture/raw/chi/graphify/mcp/04.request.json": "5212851af235eed2bd62a674fa14313ea9a38591a8d706897051badabc911735", + "exact-symbol-08/capture/raw/chi/graphify/mcp/04.response.jsonl": "d4d8b29632b3aae99cc57178a3b6ccb52935ae78e6848569b08c64f60b5a75a8", + "exact-symbol-08/capture/raw/chi/graphify/mcp/05.request.json": "ddcfc95089d296baa8b47c0889d687bd1504bf40ec9477fdd8089bfac9ff8020", + "exact-symbol-08/capture/raw/chi/graphify/mcp/05.response.jsonl": "03e89d1f3c49018cfa685aa8798dd92581aca4364a0fe9c4cb5d3a3e96ec252f", + "exact-symbol-08/capture/raw/chi/graphify/mcp/06.request.json": "f023f938c3a203f59cebf58bab070f7f55b28a6101c05335f1352481ab3516fb", + "exact-symbol-08/capture/raw/chi/graphify/mcp/06.response.jsonl": "f9beb5c0e11adba184464dd20e27d9f5d2cc2d1a080feac6b021de0c8790402f", + "exact-symbol-08/capture/raw/chi/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/chi/graphify/windows/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "exact-symbol-08/capture/raw/chi/graphify/windows/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "exact-symbol-08/capture/raw/chi/graphify/windows/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "exact-symbol-08/capture/raw/chi/graphify/windows/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "exact-symbol-08/capture/raw/chi/graphify/windows/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "exact-symbol-08/capture/raw/chi/graphify/windows/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "exact-symbol-08/capture/raw/chi/graphify/windows/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "exact-symbol-08/capture/raw/chi/graphify/windows/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "exact-symbol-08/capture/raw/chi/graphify/windows/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "exact-symbol-08/capture/raw/chi/graphify/windows/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "exact-symbol-08/capture/raw/chi/graphify/windows/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "exact-symbol-08/capture/raw/chi/graphify/windows/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "exact-symbol-08/capture/raw/chi/graphify/windows/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "exact-symbol-08/capture/raw/chi/graphify/windows/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "exact-symbol-08/capture/raw/chi/graphify/windows/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "exact-symbol-08/capture/raw/chi/graphify/windows/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "exact-symbol-08/capture/raw/chi/graphify/windows/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "exact-symbol-08/capture/raw/chi/graphify/windows/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "exact-symbol-08/capture/raw/chi/graphify/windows/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "exact-symbol-08/capture/raw/chi/graphify/windows/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "exact-symbol-08/capture/raw/chi/graphify/windows/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "exact-symbol-08/capture/raw/click/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/click/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "exact-symbol-08/capture/raw/click/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/click/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/click/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "exact-symbol-08/capture/raw/click/compass/mcp/04.request.json": "60e1587fdef73541c850a02da90047bc38daaaa290ff434dc49c9c764060ff8d", + "exact-symbol-08/capture/raw/click/compass/mcp/04.response.jsonl": "cadf5337b5ecc3d0f07dee4ac5ae685c5ae3d79c38a12ec418083df565fc2016", + "exact-symbol-08/capture/raw/click/compass/mcp/05.request.json": "a5981f00d010433776e9462d29265dd8c1e3f1ddc72acb13bca73b28101f54a8", + "exact-symbol-08/capture/raw/click/compass/mcp/05.response.jsonl": "9288557f654e4e06fa41497700b814650aaa5d059796f455bf083d37c43ce627", + "exact-symbol-08/capture/raw/click/compass/mcp/06.request.json": "6389615d7c92f8d1fff4aa1102e7a6830a879d8861610c9d3b538badd499d48d", + "exact-symbol-08/capture/raw/click/compass/mcp/06.response.jsonl": "13cd279203e5684b0748fffe6e8d3ca66e6fbae34d0ef4a0305408d733b5bbfb", + "exact-symbol-08/capture/raw/click/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/click/compass/windows/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "exact-symbol-08/capture/raw/click/compass/windows/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "exact-symbol-08/capture/raw/click/compass/windows/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "exact-symbol-08/capture/raw/click/compass/windows/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "exact-symbol-08/capture/raw/click/compass/windows/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "exact-symbol-08/capture/raw/click/compass/windows/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "exact-symbol-08/capture/raw/click/compass/windows/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "exact-symbol-08/capture/raw/click/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/click/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "exact-symbol-08/capture/raw/click/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/click/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/click/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "exact-symbol-08/capture/raw/click/graphify/mcp/04.request.json": "4aad9fe4bfc910ea3b74fa2a6bb4335c66ede5da3d24cf48609142507b61a000", + "exact-symbol-08/capture/raw/click/graphify/mcp/04.response.jsonl": "d22730f840576483c14ecb89c933817acf713e534c6a2b8b8a69ecc7e98714a2", + "exact-symbol-08/capture/raw/click/graphify/mcp/05.request.json": "f9283949d9100177a2d60e99bce661f8e92dbe8efc14cb4d3ab3f76c9326b59d", + "exact-symbol-08/capture/raw/click/graphify/mcp/05.response.jsonl": "fd6c5e880f241cf7eaccbadbfe53caaed3e77ef8388bb623c0dc5ec20f54d6a9", + "exact-symbol-08/capture/raw/click/graphify/mcp/06.request.json": "55a8fc30790c83303e0e809c7eb566223055122e5a26c5885071a26ed9850062", + "exact-symbol-08/capture/raw/click/graphify/mcp/06.response.jsonl": "fbc4566d865d13f935a1ce0c1dd6672f04f9387e43c594e05c5e1af88ad2e59d", + "exact-symbol-08/capture/raw/click/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/click/graphify/windows/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "exact-symbol-08/capture/raw/click/graphify/windows/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "exact-symbol-08/capture/raw/click/graphify/windows/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "exact-symbol-08/capture/raw/click/graphify/windows/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "exact-symbol-08/capture/raw/click/graphify/windows/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "exact-symbol-08/capture/raw/click/graphify/windows/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "exact-symbol-08/capture/raw/click/graphify/windows/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/04.request.json": "03209bfedf1902849eed749b636a24bfdf30174b0165a2186c5347a9e86c1ae5", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/04.response.jsonl": "9ccfeea5efe0917876f249b287ed70127e0d87f88ee2021e5c6dc606a8d69bbd", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/05.request.json": "678e71f511deadca622e16348bf9b0037f38612114f2bc92624fcb53b66676e0", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/05.response.jsonl": "116864610383d2d3070dd9dd7bb8eb91a1178ede5f14b90a326175247c3cb70e", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/06.request.json": "f02cf7ee1913dd1d549058f1a4e3200a1cb3753d2dcc2cc9e784020d87d3c831", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/06.response.jsonl": "c365ad586bf53567f59e2451f9505a8e5b8b3deb1ea2f91b8f95e33bab5c8bd1", + "exact-symbol-08/capture/raw/jsoup/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/jsoup/compass/windows/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "exact-symbol-08/capture/raw/jsoup/compass/windows/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "exact-symbol-08/capture/raw/jsoup/compass/windows/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "exact-symbol-08/capture/raw/jsoup/compass/windows/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "exact-symbol-08/capture/raw/jsoup/compass/windows/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "exact-symbol-08/capture/raw/jsoup/compass/windows/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "exact-symbol-08/capture/raw/jsoup/compass/windows/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "exact-symbol-08/capture/raw/jsoup/compass/windows/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/04.request.json": "883da0706262e806f7254d1763f680dbebb7917ab916867d30c4f43b2ae6b8b9", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/04.response.jsonl": "7ac5d473f020a3eef7e5f7aeb277fa5b33784b888f2d83cdb791ee569b66ec11", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/05.request.json": "33b44290f58b057bb67d81ec9e47bca5a6429c9d2d3f4a3cdf19052109786d5a", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/05.response.jsonl": "03ba969917582be251f52502cd8877f5fd9d3efc4206dea7660b64ea5958f4b0", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/06.request.json": "ec9c984810cec2fdfd8633a8ba906c052d12aeef1ba5f16dbed27c511704bcc2", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/06.response.jsonl": "809aa7cb8af3974d0dfbf64410832d5a09f0b729832295984a8565afd46afd83", + "exact-symbol-08/capture/raw/jsoup/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "exact-symbol-08/capture/raw/jsoup/graphify/windows/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "exact-symbol-08/capture/raw/redux/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/redux/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "exact-symbol-08/capture/raw/redux/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/redux/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/redux/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "exact-symbol-08/capture/raw/redux/compass/mcp/04.request.json": "5de4d816d7e1214748c0029181a97e54e90745c28cdb0dedff21b317636597f6", + "exact-symbol-08/capture/raw/redux/compass/mcp/04.response.jsonl": "b5e13c9bc3a4f5766bd24b5972e5d5803158869444a5c8c4f27afc481a6e3874", + "exact-symbol-08/capture/raw/redux/compass/mcp/05.request.json": "699af445a5be27d04e0061a3a9ec02af4594111a68f2fcd21cbf3ad824a46740", + "exact-symbol-08/capture/raw/redux/compass/mcp/05.response.jsonl": "78b7f2af44f37d40d9c7728bd88fdf1442f0dd59a80427d99afd624fa43ac9dd", + "exact-symbol-08/capture/raw/redux/compass/mcp/06.request.json": "dd5627f790fe07b2458afbac59ef35159101a16e731f958c5a1b5a7dbdf5da78", + "exact-symbol-08/capture/raw/redux/compass/mcp/06.response.jsonl": "0dc3a0e74b4a35ce129be9a1896cc5c00740caaa1e4e89aa7c05ea7f56338b11", + "exact-symbol-08/capture/raw/redux/compass/mcp/07.request.json": "7f325ed609e0b5566f68fc1afa4d56ee6ea4b17b9cf637aa35f2ad87437f4afe", + "exact-symbol-08/capture/raw/redux/compass/mcp/07.response.jsonl": "484d9b3a10f0e3a9a3bef7f38db457640e12463a642efe44a8b063bedf5c20ff", + "exact-symbol-08/capture/raw/redux/compass/mcp/08.request.json": "964bed0361814bdeee4c195a4dbd5636944a020232a5c3228845d7b70b834cb2", + "exact-symbol-08/capture/raw/redux/compass/mcp/08.response.jsonl": "feb194ebac4bb6527224048e08e29446df490d750712dcb097b1e6803649ee95", + "exact-symbol-08/capture/raw/redux/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/redux/compass/windows/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "exact-symbol-08/capture/raw/redux/compass/windows/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "exact-symbol-08/capture/raw/redux/compass/windows/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "exact-symbol-08/capture/raw/redux/compass/windows/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "exact-symbol-08/capture/raw/redux/compass/windows/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "exact-symbol-08/capture/raw/redux/compass/windows/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "exact-symbol-08/capture/raw/redux/compass/windows/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "exact-symbol-08/capture/raw/redux/compass/windows/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "exact-symbol-08/capture/raw/redux/compass/windows/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "exact-symbol-08/capture/raw/redux/compass/windows/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "exact-symbol-08/capture/raw/redux/compass/windows/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "exact-symbol-08/capture/raw/redux/compass/windows/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "exact-symbol-08/capture/raw/redux/compass/windows/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "exact-symbol-08/capture/raw/redux/compass/windows/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "exact-symbol-08/capture/raw/redux/compass/windows/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "exact-symbol-08/capture/raw/redux/compass/windows/015.source": "2fa50459f3adaa2c60d3d328d13ddb749207e27feb3c92fe43812e9f2edc01c6", + "exact-symbol-08/capture/raw/redux/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/redux/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "exact-symbol-08/capture/raw/redux/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/redux/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/redux/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "exact-symbol-08/capture/raw/redux/graphify/mcp/04.request.json": "c32cfb2450c017920145bae06b04af6f131c02499e30d987f76daf8e53f1a5e9", + "exact-symbol-08/capture/raw/redux/graphify/mcp/04.response.jsonl": "cba36e66dcc88f9313662b40b072d5c24cf9fbf25310ac3b1c2f4eaebc8e02c6", + "exact-symbol-08/capture/raw/redux/graphify/mcp/05.request.json": "d09ec5f892edc9b5060a5c26c87e8f17c71cbfde6c12d4d531c783009a1571e1", + "exact-symbol-08/capture/raw/redux/graphify/mcp/05.response.jsonl": "1e48f4a2d7cd27755a2d0d75cfde9d97c6556e720ac2e341ad33db57a1529b65", + "exact-symbol-08/capture/raw/redux/graphify/mcp/06.request.json": "cc1a38cf606bc4c2aebb49742322ce284f708e44a3d49246b05d4ecddbb9fd5c", + "exact-symbol-08/capture/raw/redux/graphify/mcp/06.response.jsonl": "121e650b10614f3521ee06ed0a7caff654bf7640d1fba6d6d583de997a08f881", + "exact-symbol-08/capture/raw/redux/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/redux/graphify/windows/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "exact-symbol-08/capture/raw/redux/graphify/windows/001.source": "104842ff0af72b77928d0a849bc5aa5e5bf080e95aeca8f8bc0baacd3dd90aa1", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/04.request.json": "6731b10af0227cd2c72027c483067f2f6c66e5132cc795e94c526b2621dce8b4", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/04.response.jsonl": "059aeda7dc5b8db56e899c21607f48a9a155d27af5ad9de59a847b4389947eb3", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/05.request.json": "c529b2d84f61070e4b8e529556ae15058513f80f2e5f95ecaa9d6ba21c7bb2c5", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/05.response.jsonl": "799db45137a079944ca35821d15c2080d77a3933041bec763188dc7960dfee47", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/06.request.json": "351ffaebecafa6ec3d3b75f67301f651bd8235ec0931203df270a33b35c57bcb", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/06.response.jsonl": "8b25fa3d0fc64cadfc4699a6fadbc54a1c780c7e27d8c03dc784faf4f333d016", + "exact-symbol-08/capture/raw/walkdir/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/walkdir/compass/windows/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "exact-symbol-08/capture/raw/walkdir/compass/windows/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "exact-symbol-08/capture/raw/walkdir/compass/windows/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "exact-symbol-08/capture/raw/walkdir/compass/windows/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "exact-symbol-08/capture/raw/walkdir/compass/windows/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "exact-symbol-08/capture/raw/walkdir/compass/windows/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "exact-symbol-08/capture/raw/walkdir/compass/windows/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "exact-symbol-08/capture/raw/walkdir/compass/windows/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "exact-symbol-08/capture/raw/walkdir/compass/windows/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "exact-symbol-08/capture/raw/walkdir/compass/windows/009.source": "907ef6bd190a9985da364ec084579cacae6b06067d5a3bdfa8d98a2c3256eb9b", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/04.request.json": "ac9506fc18f6f6cf468a46a9e781188b01156b4b7c6ab64ad601ab06640a8c25", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/04.response.jsonl": "2190ecdb65f6a8ba2da85c820eccc0fdd4f5c647cedd341f1d624e1368c0977e", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/05.request.json": "65c2b9b1c1a27b16b80c79f218792a08426c4816c596679d0d57190ece2f87e5", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/05.response.jsonl": "8b3d0bf2593d26ad5f4914c6ca5a2da3d546495d6ad44b659fae6db89f914d98", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/06.request.json": "e0c2889dd22e5bf4dcb7bfbd1988c411c468102dcd3171bccc1839bef002c67b", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/06.response.jsonl": "4414636125991fe30a997d1431d766c61638dd0aacd3de29c97cc6fa11c08d3b", + "exact-symbol-08/capture/raw/walkdir/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "exact-symbol-08/capture/raw/walkdir/graphify/windows/005.source": "b0c0b5e23f7d4cf98d355ff7f08101ea772f51b09b278f51ab7bb66ad2aaf4a6", + "exact-symbol-08/capture/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "exact-symbol-08/capture/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "exact-symbol-08/capture.log": "7942afdcda3ed01a7a1d851b7ebb495fcc52bbd2eb3caa938a3c989e7740137c", + "exact-symbol-08/cli-query-tests-final.log": "ced998b6596679b097d1d01b7e617b7d28a5d5acabbe005057d62bed90f82733", + "exact-symbol-08/clippy-final.log": "49ffaeaceeba3d02bf3df5eedbc0408b69a356da5e924d56fba5519fd66a30af", + "exact-symbol-08/collect.py": "44fc18ff82318dd82b3de106d4a8c8bc55f2d882eb8badf5e1699c43493fb8bd", + "exact-symbol-08/fmt-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/mcp-tests-final.log": "97a746b70c66d0335eb132fed03db34c164ecd51efb4adb61bef13e11fc19d7b", + "exact-symbol-08/output-tests-final.log": "5b9f76bf7c3ff9dbf360218e8ea3a64e9c4402249c6a4494ee28f335cbc5c4b1", + "exact-symbol-08/product-boundary-final.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "exact-symbol-08/product-tests-final.log": "97cfb0fd95e80663cc5a9fb2a98ae5aab534ebc898be1447fe39f75b0b47794f", + "exact-symbol-08/query-tests-final.log": "7bf34bbd052c4f89e78f888ac8df99433ec6072b4d6871c55a7874bfdba9f75b", + "exact-symbol-08/validate.py": "0136921ad1abff40d4049185c31a3709fc09fca560f297829ba603bae308594b", + "exact-symbol-08/validation.json": "e6f433eecccde7fc0e27873fc2fb63d7ecb9479a4c986c1ad09b097fd172b1b7", + "exact-symbol-08/verification.log": "e8c0969f1e23ab63fc159ad1687f368e6ea3064674062e0334a5e15ea8c24e99", + "exact-symbol-08/verified-summary.json": "491b33b8387939ca82d44b6baa32683333689cbdc327183a7894119168f2dee7", + "exact-symbol-08/verify_capture.py": "fe01411d464f9a216e010a44b66706fe85bc0e1de3cf44ec29154186d43a014e", + "exact-symbol-08/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "exact-symbol-08/workspace-tests-final.log": "14362090d14b3654e0e2efee13379b1337ca0db98fecb6e52bea3eb2006c8bba", + "exact-symbol-08/write_review.py": "8c5c4ea53179bd519fd0eb32128448cce741adebeec3e4e0964e551a056d578f" + }, + "limitations": [ + "Known five-subject development panel; no held-out confirmation or representative precision claim.", + "Same-agent source review with a separate verifier, not independent human adjudication.", + "All 153 returned membership anchors were checked as declaration, binding or property sites; this is not a review of every returned non-membership edge.", + "The shared source policy can include code between anchors, and the tools expose different anchor sets. It is not a best-possible workflow or equal-compute comparison.", + "Exact lookup filters after the bounded name-index read. Names exceeding the candidate limit remain incomplete even if a specific file filter would eventually be unique.", + "No authored responsibility explanations, god-object defect judgments, broad caller/callee precision, longer-walk superiority or universal dominance is established." + ] +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index a4d050fd9..d91ad2e17 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2616,6 +2616,96 @@ artifacts are under `member-source-02`. The next explanation work needs better source context and selection, actual responsibility synthesis, and fresh confirmation. This result does not establish overall superiority. +## Exact source-constrained lookup and the remaining retrieval gap + +Registration `2ef40e95` keeps the same five repositories, graph/source pins and +20 facts, and adds explicit identity constraints: symbol, source file, declaration +start line and stored kind. These are supplied task inputs in a new development +arm; they do not retroactively rescue the old unscoped Redux result. Prior +sources and outputs were known when this arm was designed. + +Implementation `9cc6ba9e` adds `search --exact` and MCP `search_symbols` with +`exact: true`, plus optional file, start-line and kind filters. Exact IDs take +precedence; otherwise the existing normalized name index is read within the +unchanged candidate cap. All matching records survive unless an explicit filter +excludes them. No lexical fallback, inferred ownership, export-binding collapse, +or arbitrary winner is introduced. Filtering happens after the bounded index +read: a truncated singleton or empty result proves neither uniqueness nor +absence. Exact text requests keep the supplied bounds without automatic widening. + +The paired identity control uses the same supplied constraints. Compass receives +its supported fields directly; Graphify receives its existing `file::symbol` +lookup, and its returned source identity and declaration kind are checked against +the constraints afterward. Graphify is not claimed to accept a native kind +filter. Each tool receives one scored lookup, followed by one neighbor request +and the unchanged registered 8,000-byte source-window policy. Diagnostic replays +and negative controls are kept separate from those scored calls. + +Both tools resolve **5/5 subjects**. Compass now reaches the `createStore` +function at `src/createStore.ts:86`. Omitting the explicit `function` kind returns +both its declaration and its coincident export record; neither is silently +removed. Asking for declaration line 87 returns no match with complete lookup. +The original ten unscoped resolver response payloads are unchanged, including +Compass's old candidate-limit failure. All nine previously available neighbor +responses and source-window payloads are also unchanged. + +| Subject | Compass constrained lookup + windows | Graphify constrained lookup + windows | +| --- | ---: | ---: | +| Chi / Go | 3/4 | 3/4 | +| Click / Python | 4/4 | 4/4 | +| jsoup / Java | 3/4 | 4/4 | +| Redux / TypeScript | 3/4 | 2/4 | +| WalkDir / Rust | 1/4 | 2/4 | +| **Complete facts supported by source** | **14/20** | **15/20** | + +The three newly supported Compass facts concern Redux enhancer behavior, +dispatch state/reentrancy, and listener snapshots/unsubscription. Its returned +parameter anchors start early enough to include the enhancer body; Graphify's +first returned member anchor is later. Compass's final window reaches part of +line 312, and the observable/store-API fact remains unavailable. Graphify still +supplies one additional Java fact and one additional Rust fact. Thus the overall +lead in this workflow remains Graphify's, despite Compass's Redux advantage. + +Strict literal witness counts are 13/20 and 14/20. Both semantic scores retain +the prior Click allowance: the missing class-header line is supplied by the +independently verified owner identity, and the initialization, decorator and +getter code are all present. Every other credited fact has complete literal +witnesses. Neither tool authors the mechanism answers; these are source-evidence +scores, not synthesized explanations or god-object judgments. The earlier +11/20 versus 15/20 unscoped-window result and 13/20 native member-mode result +remain separate. + +Both tools now return 35,673 source bytes across the five subjects. Compass's +neighbor responses contain 33,061 text bytes and 372,829 full MCP response bytes; +Graphify's contain 6,980 and 7,534. Scored resolver responses add 3,039 text / +21,983 wire bytes for Compass and 619 / 1,094 for Graphify. This is not equal +compute, latency or response-size superiority. The verifier checks all 32 saved +public calls, pinned inputs, source windows and unchanged earlier payloads. +The additional 23 Redux membership anchors were source-reviewed, bringing the +reviewed membership-site total to 153; other relationship precision is unproven. + +Native checks exposed and corrected two response defects during development: +new exact-mode node limits initially retained excess search hits, and an empty +truncated search failed Agent View validation by claiming `no_match` without a +no-match diagnostic. Exact mode now bounds nodes and hits together; the view +preserves unknown match with partial execution. Search display also uses the +query engine's existing name normalization. Failed fixture construction and +overly strict test assertions are retained separately from these product defects. + +Final validation passed formatting, 37 targeted query/backend/binding tests, +20 output-contract tests, 41 CLI tests, 61 MCP tests, workspace Clippy, +1,106 workspace tests (2 ignored), 9 product tests, the product boundary and +CLI build. Validated source hashes match `9cc6ba9e`; evaluated and final binaries +match. JavaScript/viewer and extraction/publication gates were not rerun because +those surfaces are unchanged. Version stays 0.3.30. The separate same-agent +artifact verifier passes; this is not independent human adjudication. + +Per-fact judgments, command logs, failed attempts and artifact hashes are recorded +in `benchmarks/agent_query/exact_symbol_development_review.json`, with external +artifacts under `exact-symbol-01` through `exact-symbol-08`. Source selection, +responsibility synthesis, actual god-object defect evidence, broader edge +precision, longer walks and fresh confirmation remain unfinished. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 1c6f2a43a59c4d8db642a64424abf672e8415099 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:35:36 -0700 Subject: [PATCH 75/97] Register shared-state access evidence audit across five languages --- ...state_access_development_registration.json | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 benchmarks/agent_query/state_access_development_registration.json diff --git a/benchmarks/agent_query/state_access_development_registration.json b/benchmarks/agent_query/state_access_development_registration.json new file mode 100644 index 000000000..ff4e0daa6 --- /dev/null +++ b/benchmarks/agent_query/state_access_development_registration.json @@ -0,0 +1,321 @@ +{ + "schema": "compass.state-access-development-registration/1", + "baselineCommit": "7aef6a0c41dd82d3db0a1a74aff1d5c7f956c363", + "scope": "Known development subjects; prior graph inventories were observed, including missing reads/writes. Source-selected diagnostic, not blinded, held-out, representative, a query benchmark, or a god-object classifier.", + "graphRun": "rust-index-receiver-03/run.json", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "protocol": { + "denominator": "20 explicit state-access sites, two state slots with two different accessing methods in each of five repositories. Java includes a nested visitor; Redux uses closure variables, not object fields. Python first assignments serve as field introduction witnesses. These are not equivalent class-level cohesion samples.", + "identity": "Resolve callable and state nodes using exact pinned file, source declaration line and source symbol identity. Preserve all candidates; parameter shadowing and export/coincident nodes must not be silently collapsed. Missing or ambiguous endpoints remain failures for directly usable state evidence.", + "coverage": "For a uniquely identified callable and state slot, retain ALL graph records between the endpoints in either direction. A forward references/reads/writes edge is generic state-contact evidence. Separately require occurrence provenance matching the selected source access line. Calls to a field value type, owner type references, node containment, member names alone, and source snippets do not constitute state-access edges. Report absent state nodes separately from absent access links; do not interpret either as independence or low cohesion.", + "bounds": "Use the complete frozen graph (max 512 MiB each) equally for both tools. This is a representation diagnostic, not public query success, timing or response-efficiency evidence. Save exact candidate nodes and connecting records, input hashes and replayable verifier.", + "limitations": [ + "No interprocedural dataflow or runtime object identity inference.", + "No read/write precision score; receiver mutation is not automatically a field assignment.", + "Same-agent source judgment; no independent adjudication.", + "No god-object, LCOM, whole-class cohesion, extraction precision or broad superiority claim." + ] + }, + "cases": [ + { + "repository": "chi", + "commit": "3d1777a1ef8881f7d1da0b02c76ca8f0a29cd2bc", + "file": "mux.go", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "groups": [ + { + "owner": "Mux", + "state": "handler", + "declarationLine": 24, + "declarationText": "\thandler http.Handler", + "accesses": [ + { + "method": "Use", + "methodLine": 100, + "line": 101, + "column": 5, + "expression": "mx.handler", + "operation": "read", + "text": "\tif mx.handler != nil {" + }, + { + "method": "With", + "methodLine": 242, + "line": 245, + "column": 19, + "expression": "mx.handler", + "operation": "read", + "text": "\tif !mx.inline && mx.handler == nil {" + } + ] + }, + { + "owner": "Mux", + "state": "middlewares", + "declarationLine": 43, + "declarationText": "\tmiddlewares []func(http.Handler) http.Handler", + "accesses": [ + { + "method": "Use", + "methodLine": 100, + "line": 104, + "column": 2, + "expression": "mx.middlewares", + "operation": "write", + "text": "\tmx.middlewares = append(mx.middlewares, middlewares...)" + }, + { + "method": "With", + "methodLine": 242, + "line": 252, + "column": 31, + "expression": "mx.middlewares", + "operation": "read", + "text": "\t\tmws = make(Middlewares, len(mx.middlewares))" + } + ] + } + ] + }, + { + "repository": "click", + "commit": "06b2a678741131fd577ce170e23e5ca0aeba0309", + "file": "src/click/_compat.py", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "groups": [ + { + "owner": "_AtomicFile", + "state": "closed", + "declarationLine": 460, + "declarationText": " self.closed = False", + "accesses": [ + { + "method": "__init__", + "methodLine": 456, + "line": 460, + "column": 9, + "expression": "self.closed", + "operation": "write", + "text": " self.closed = False" + }, + { + "method": "close", + "methodLine": 466, + "line": 467, + "column": 12, + "expression": "self.closed", + "operation": "read", + "text": " if self.closed:" + } + ] + }, + { + "owner": "_AtomicFile", + "state": "_real_filename", + "declarationLine": 459, + "declarationText": " self._real_filename = real_filename", + "accesses": [ + { + "method": "__init__", + "methodLine": 456, + "line": 459, + "column": 9, + "expression": "self._real_filename", + "operation": "write", + "text": " self._real_filename = real_filename" + }, + { + "method": "name", + "methodLine": 463, + "line": 464, + "column": 16, + "expression": "self._real_filename", + "operation": "read", + "text": " return self._real_filename" + } + ] + } + ] + }, + { + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "groups": [ + { + "owner": "Cleaner", + "state": "safelist", + "declarationLine": 44, + "declarationText": " private final Safelist safelist;", + "accesses": [ + { + "method": "Cleaner", + "methodLine": 50, + "line": 52, + "column": 9, + "expression": "this.safelist", + "operation": "write", + "text": " this.safelist = safelist;" + }, + { + "method": "createSafeElement", + "methodLine": 188, + "line": 197, + "column": 17, + "expression": "safelist", + "operation": "read", + "text": " if (safelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) { // will keep this attr" + } + ] + }, + { + "owner": "CleaningVisitor", + "state": "destination", + "declarationLine": 141, + "declarationText": " private Element destination; // current element to append nodes to", + "accesses": [ + { + "method": "CleaningVisitor", + "methodLine": 143, + "line": 145, + "column": 13, + "expression": "this.destination", + "operation": "write", + "text": " this.destination = destination;" + }, + { + "method": "head", + "methodLine": 148, + "line": 158, + "column": 21, + "expression": "destination", + "operation": "write", + "text": " destination = destChild;" + } + ] + } + ] + }, + { + "repository": "redux", + "commit": "3ae0f79bdcce35ac2a4895e284ef04b7dc0ebd5e", + "file": "src/createStore.ts", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "groups": [ + { + "owner": "createStore", + "state": "currentState", + "declarationLine": 137, + "declarationText": " let currentState: S | PreloadedState | undefined = preloadedState as", + "accesses": [ + { + "method": "getState", + "methodLine": 166, + "line": 175, + "column": 12, + "expression": "currentState", + "operation": "read", + "text": " return currentState as S" + }, + { + "method": "dispatch", + "methodLine": 270, + "line": 299, + "column": 7, + "expression": "currentState", + "operation": "write", + "text": " currentState = currentReducer(currentState, action)" + } + ] + }, + { + "owner": "createStore", + "state": "isDispatching", + "declarationLine": 143, + "declarationText": " let isDispatching = false", + "accesses": [ + { + "method": "getState", + "methodLine": 166, + "line": 167, + "column": 9, + "expression": "isDispatching", + "operation": "read", + "text": " if (isDispatching) {" + }, + { + "method": "dispatch", + "methodLine": 270, + "line": 298, + "column": 7, + "expression": "isDispatching", + "operation": "write", + "text": " isDispatching = true" + } + ] + } + ] + }, + { + "repository": "walkdir", + "commit": "6fd031c82ba5a4204b4ce6eae73dacb00dc072ec", + "file": "src/lib.rs", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "groups": [ + { + "owner": "IntoIter", + "state": "deferred_dirs", + "declarationLine": 598, + "declarationText": " deferred_dirs: Vec,", + "accesses": [ + { + "method": "handle_entry", + "methodLine": 840, + "line": 875, + "column": 13, + "expression": "self.deferred_dirs", + "operation": "receiver", + "text": " self.deferred_dirs.push(dent);" + }, + { + "method": "get_deferred_dir", + "methodLine": 884, + "line": 886, + "column": 29, + "expression": "self.deferred_dirs", + "operation": "read", + "text": " if self.depth < self.deferred_dirs.len() {" + } + ] + }, + { + "owner": "IntoIter", + "state": "oldest_opened", + "declarationLine": 591, + "declarationText": " oldest_opened: usize,", + "accesses": [ + { + "method": "push", + "methodLine": 901, + "line": 945, + "column": 13, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = self.oldest_opened.checked_add(1).unwrap();" + }, + { + "method": "pop", + "methodLine": 950, + "line": 958, + "column": 9, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = min(self.oldest_opened, self.stack_list.len());" + } + ] + } + ] + } + ] +} From 9cdeab6a1b0ba1d75179453145dff0b2b48e0aa7 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:42:40 -0700 Subject: [PATCH 76/97] Audit shared-state evidence gaps across five real repositories --- benchmarks/agent_query/README.md | 27 + benchmarks/agent_query/state_access_audit.py | 167 + .../state_access_development_review.json | 2815 +++++++++++++++++ .../agent_query/tests/test_state_access.py | 111 + ...ode-graph-intelligence-audit-2026-09-26.md | 81 + 5 files changed, 3201 insertions(+) create mode 100644 benchmarks/agent_query/state_access_audit.py create mode 100644 benchmarks/agent_query/state_access_development_review.json create mode 100644 benchmarks/agent_query/tests/test_state_access.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 7ee7d92af..c1968ab0e 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -481,3 +481,30 @@ for each. All 15 Compass full-record projections match their graphs, preserving 181 record appearances. This is graph consistency, not source precision for all records. The extra source coordinates, richer Compass payload cost and reused development scope remain explicit; no overall superiority claim follows. + + +### State-access evidence prerequisite + +`state_access_development_registration.json` fixes 20 source access sites across +Go, Python, Java, TypeScript and Rust. This known-subject diagnostic inspects full +frozen graphs, not public query output or a god-object classifier. Both tools +lack all 20 selected state-contact links; Compass represents six of ten state +slots and Graphify none at the pinned coordinates. Missing edges must not be +interpreted as method independence or low cohesion. + +Replay the committed per-site results against the original external artifacts: + +```sh +python3 -m benchmarks.agent_query.state_access_audit \ + --registration benchmarks/agent_query/state_access_development_registration.json \ + --artifact-root /path/to/code-graph-audit-20260926 \ + --output benchmarks/agent_query/state_access_development_review.json --verify +python3 -m unittest benchmarks.agent_query.tests.test_state_access +``` + +Omit `--verify` with a new output path to produce a fresh report. Existing reports +are never overwritten. The verifier checks source commits and witnesses, graph +hashes, all candidate/connecting records and the auditor's own code hash. It +preserves Graphify's undirected container flag; stored endpoint order is not a +native directed-path claim. Same-agent review and purposive development scope +remain explicit. diff --git a/benchmarks/agent_query/state_access_audit.py b/benchmarks/agent_query/state_access_audit.py new file mode 100644 index 000000000..a68dd2df6 --- /dev/null +++ b/benchmarks/agent_query/state_access_audit.py @@ -0,0 +1,167 @@ +"""Replay a source-registered, full-graph state-contact coverage diagnostic. + +This does not infer read/write effects, runtime aliasing, LCOM, or god objects. +Frozen graphs are compared equally; no public-query success is claimed. +""" +import argparse +from collections import Counter +import hashlib +import json +from pathlib import Path +import re +import subprocess + +MAX_GRAPH_BYTES = 512 * 1024 * 1024 +MAX_SOURCE_BYTES = 4 * 1024 * 1024 + + +def read(path, limit): + with path.open('rb') as stream: + data = stream.read(limit + 1) + if len(data) > limit: + raise ValueError(f'input exceeds {limit} bytes: {path}') + return data + + +def digest(data): + return hashlib.sha256(data).hexdigest() + + +def anchor(record, tool): + if tool == 'compass': + source = record.get('source', {}) + return source.get('file'), source.get('startLine') + match = re.fullmatch(r'L([1-9][0-9]*)(?:-L?[1-9][0-9]*)?', record.get('source_location', '')) + return record.get('source_file'), int(match[1]) if match else None + + +def name(record, tool): + return record.get('name' if tool == 'compass' else 'label', '').removeprefix('.').removesuffix('()') + + +def candidates(nodes, tool, file, line, symbol, *, constructor=False): + return sorted((n for n in nodes if anchor(n, tool) == (file, line) + and (name(n, tool) == symbol or + (constructor and n.get('kind') == 'constructor' and name(n, tool) == ''))), + key=lambda n: n['id']) + + +def occurrence(edge, tool, file, line): + if tool == 'compass': + # The relationship site is the edge occurrence; declaration evidence + # elsewhere on the record is not a substitute. + site = edge.get('relationshipSite', {}) + return (site.get('file'), site.get('startLine')) == (file, line) + return anchor(edge, tool) == (file, line) + + +def assess(graph, tool, case, group, access): + nodes = graph['nodes'] + methods = candidates(nodes, tool, case['file'], access['methodLine'], access['method'], + constructor=access['method'] == group['owner']) + states = candidates(nodes, tool, case['file'], group['declarationLine'], group['state']) + method_ids = {n['id'] for n in methods} + state_ids = {n['id'] for n in states} + connecting = [e for e in graph['links'] if + (e['source'] in method_ids and e['target'] in state_ids) or + (e['source'] in state_ids and e['target'] in method_ids)] + connecting.sort(key=lambda e: json.dumps(e, sort_keys=True)) + contacts = [e for e in connecting if e['source'] in method_ids and e['target'] in state_ids + and e.get('kind' if tool == 'compass' else 'relation') in {'references', 'reads', 'writes'}] + unique = len(methods) == len(states) == 1 + status = ('missing_callable' if not methods else 'ambiguous_callable' if len(methods) != 1 else + 'missing_state' if not states else 'ambiguous_state' if len(states) != 1 else + 'no_contact_edge' if not contacts else 'contact_edge') + return dict(repository=case['repository'], tool=tool, graphDirected=graph.get('directed'), owner=group['owner'], + state=group['state'], file=case['file'], declarationLine=group['declarationLine'], + access=access, methodCandidates=methods, stateCandidates=states, + connectingRecords=connecting, status=status, + contactSupported=unique and bool(contacts), + selectedLineSupported=unique and any(occurrence(e, tool, case['file'], access['line']) for e in contacts)) + + +def git(root, *args): + return subprocess.check_output(['git', '-C', str(root), *args], text=True, timeout=30).strip() + + +def evaluate(registration, artifact_root): + raw = read(registration, MAX_SOURCE_BYTES) + reg = json.loads(raw) + if reg['schema'] != 'compass.state-access-development-registration/1': + raise ValueError('unknown registration schema') + run_data = read(artifact_root / reg['graphRun'], MAX_SOURCE_BYTES) + if digest(run_data) != reg['graphRunSha256']: + raise ValueError('graph run hash mismatch') + repos = {r['repository']: r for r in json.loads(run_data)['repositories']} + rows, inputs = [], [] + for case in reg['cases']: + repo = repos[case['repository']] + root = Path(repo['source']).resolve() + path = (root / case['file']).resolve() + path.relative_to(root) + if git(root, 'rev-parse', 'HEAD') != case['commit'] or git(root, 'status', '--porcelain'): + raise ValueError('source checkout pin/status mismatch') + source = read(path, MAX_SOURCE_BYTES) + if digest(source) != case['sourceFileSha256']: + raise ValueError('source hash mismatch') + lines = source.decode('utf-8').splitlines() + for group in case['groups']: + if lines[group['declarationLine']-1] != group['declarationText']: + raise ValueError('declaration witness mismatch') + for a in group['accesses']: + if lines[a['line']-1] != a['text'] or not a['text'][a['column']-1:].startswith(a['expression']): + raise ValueError('access witness mismatch') + if not re.search(r'\b' + re.escape(a['method']) + r'\s*[<(]', lines[a['methodLine']-1]): + raise ValueError('method declaration witness mismatch') + for tool in ('compass', 'graphify'): + data = read(Path(repo[tool+'Graph']), MAX_GRAPH_BYTES) + if digest(data) != repo[tool+'GraphSha256']: + raise ValueError('graph hash mismatch') + graph = json.loads(data) + identifiers = {n['id'] for n in graph['nodes']} + if len(identifiers) != len(graph['nodes']): + raise ValueError('requires unique graph node IDs') + if any(e['source'] not in identifiers or e['target'] not in identifiers for e in graph['links']): + raise ValueError('dangling graph endpoint') + inputs.append(dict(repository=case['repository'], tool=tool, graphSha256=digest(data), + sourceFileSha256=digest(source), graphDirected=graph.get('directed'), bytes=len(data), + nodes=len(graph['nodes']), edges=len(graph['links']))) + rows.extend(assess(graph, tool, case, group, access) + for group in case['groups'] for access in group['accesses']) + # Source files are read-only throughout; do not accept concurrent drift. + if digest(read(path, MAX_SOURCE_BYTES)) != digest(source) or git(root, 'rev-parse', 'HEAD') != case['commit'] or git(root, 'status', '--porcelain'): + raise ValueError('source changed during audit') + summary = {} + for tool in ('compass', 'graphify'): + selected = [r for r in rows if r['tool'] == tool] + summary[tool] = dict(accessSites=len(selected), + uniqueCallableSites=sum(len(r['methodCandidates']) == 1 for r in selected), + uniqueStateSlots=len({(r['repository'], r['owner'], r['state']) for r in selected if len(r['stateCandidates']) == 1}), + contactSupported=sum(r['contactSupported'] for r in selected), + selectedLineSupported=sum(r['selectedLineSupported'] for r in selected), + status=dict(sorted(Counter(r['status'] for r in selected).items()))) + return dict(schema='compass.state-access-development-review/1', registrationSha256=digest(raw), + auditScriptSha256=digest(read(Path(__file__), MAX_SOURCE_BYTES)), + graphRunSha256=reg['graphRunSha256'], summary=summary, inputs=inputs, results=rows) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--registration', type=Path, required=True) + parser.add_argument('--artifact-root', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--verify', action='store_true', help='recompute and compare an existing result') + args = parser.parse_args() + report = evaluate(args.registration, args.artifact_root) + payload = (json.dumps(report, indent=2) + '\n').encode() + if args.verify: + if read(args.output, MAX_GRAPH_BYTES) != payload: + raise ValueError('saved review differs from recomputed evidence') + else: + with args.output.open('xb') as stream: + stream.write(payload) + print(json.dumps(report['summary'], indent=2)) + + +if __name__ == '__main__': + main() diff --git a/benchmarks/agent_query/state_access_development_review.json b/benchmarks/agent_query/state_access_development_review.json new file mode 100644 index 000000000..4a054e931 --- /dev/null +++ b/benchmarks/agent_query/state_access_development_review.json @@ -0,0 +1,2815 @@ +{ + "schema": "compass.state-access-development-review/1", + "registrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "auditScriptSha256": "2bf95b707f18f34c9df4461a26f011b83d86f166efacfaab652f8e6231161058", + "graphRunSha256": "9a6e670114f5be83773176e54bf0c4faf45582389164dea79ad17306eee1c50c", + "summary": { + "compass": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 6, + "contactSupported": 0, + "selectedLineSupported": 0, + "status": { + "missing_state": 8, + "no_contact_edge": 12 + } + }, + "graphify": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 0, + "contactSupported": 0, + "selectedLineSupported": 0, + "status": { + "missing_state": 20 + } + } + }, + "inputs": [ + { + "repository": "chi", + "tool": "compass", + "graphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "graphDirected": true, + "bytes": 2488750, + "nodes": 729, + "edges": 1914 + }, + { + "repository": "chi", + "tool": "graphify", + "graphSha256": "498c40cd157af3d6543eab69094c8f6ecd9ed58962b44f84e7c03c3270fbfacf", + "sourceFileSha256": "cc44c2d620e6306b16d6d80f5f6c70f02b5814b357a4f5823372818f355ae67d", + "graphDirected": false, + "bytes": 1066128, + "nodes": 674, + "edges": 2563 + }, + { + "repository": "click", + "tool": "compass", + "graphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "graphDirected": true, + "bytes": 9607995, + "nodes": 4264, + "edges": 6387 + }, + { + "repository": "click", + "tool": "graphify", + "graphSha256": "4b93f626310bcab3e80504d7e031f300020c801e86cdeed3d261b5c984767234", + "sourceFileSha256": "8db1da2965cf3e5dc66c6c53833cbc613ea1cd2440b9d0eb6aa8360eb46d4913", + "graphDirected": false, + "bytes": 2855866, + "nodes": 2867, + "edges": 5257 + }, + { + "repository": "jsoup", + "tool": "compass", + "graphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "graphDirected": true, + "bytes": 26063627, + "nodes": 6116, + "edges": 21110 + }, + { + "repository": "jsoup", + "tool": "graphify", + "graphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69", + "sourceFileSha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d", + "graphDirected": false, + "bytes": 11153389, + "nodes": 5361, + "edges": 20456 + }, + { + "repository": "redux", + "tool": "compass", + "graphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "graphDirected": true, + "bytes": 8047006, + "nodes": 3503, + "edges": 5653 + }, + { + "repository": "redux", + "tool": "graphify", + "graphSha256": "52f619f953274059ab168869b6b138d4b5d64d66383b459875f61579c981749b", + "sourceFileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695", + "graphDirected": false, + "bytes": 902536, + "nodes": 996, + "edges": 1597 + }, + { + "repository": "walkdir", + "tool": "compass", + "graphSha256": "e68fbe798dcf7422184736971dcdfc29d577e1e37269ea9c43456b9f6af54cb3", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "graphDirected": true, + "bytes": 1316088, + "nodes": 288, + "edges": 1206 + }, + { + "repository": "walkdir", + "tool": "graphify", + "graphSha256": "338587967603d146bbd7e2c7d07f99313c62c5b9f2a7d430dc56d094bbb709fd", + "sourceFileSha256": "3f7d673f9e278a71de2cb5f90353a44ea7803a98d49c2f72a68cb26dce8c966a", + "graphDirected": false, + "bytes": 212674, + "nodes": 247, + "edges": 457 + } + ], + "results": [ + { + "repository": "chi", + "tool": "compass", + "graphDirected": true, + "owner": "Mux", + "state": "handler", + "file": "mux.go", + "declarationLine": 24, + "access": { + "method": "Use", + "methodLine": 100, + "line": 101, + "column": 5, + "expression": "mx.handler", + "operation": "read", + "text": "\tif mx.handler != nil {" + }, + "methodCandidates": [ + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "kind": "method", + "name": ".Use()", + "qualifiedName": "chi.Mux::Use", + "language": "go", + "source": { + "file": "mux.go", + "startByte": 3098, + "endByte": 3323, + "startLine": 100, + "startColumn": 0, + "endLine": 105, + "endColumn": 1 + }, + "details": { + "type": "symbol", + "data": { + "signature": "func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler)", + "signatureDigest": "659f46b12b16aad7608083957e6fae567b04419465f28ead26a3fed332042e5e", + "implementationDigest": "90b90e0dc2512b2eaa1dde6c97ce763901114da265768319579d335e0f808dd5", + "sourceDigest": "245aefc44e489ad148dd12f50ed3d4cc4c2906ac1f0a7f9910971c08d775e488" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.go.universal", + "confidence": "exact", + "anchors": [ + { + "file": "mux.go", + "startByte": 3113, + "endByte": 3116, + "startLine": 100, + "startColumn": 15, + "endLine": 100, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 0, + "label": "NewRouter" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "compass", + "graphDirected": true, + "owner": "Mux", + "state": "handler", + "file": "mux.go", + "declarationLine": 24, + "access": { + "method": "With", + "methodLine": 242, + "line": 245, + "column": 19, + "expression": "mx.handler", + "operation": "read", + "text": "\tif !mx.inline && mx.handler == nil {" + }, + "methodCandidates": [ + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "kind": "method", + "name": ".With()", + "qualifiedName": "chi.Mux::With", + "language": "go", + "source": { + "file": "mux.go", + "startByte": 7987, + "endByte": 8669, + "startLine": 242, + "startColumn": 0, + "endLine": 263, + "endColumn": 1 + }, + "details": { + "type": "symbol", + "data": { + "signature": "func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router", + "signatureDigest": "76785d02f71f43d41c2a72a89469f4245987421904054cfd01ea9d57b1df7446", + "implementationDigest": "67bd9b2192bc28c5f2e6fef637fa8ff9173a41dbe1406c1241cdaeae7b15cb37", + "sourceDigest": "c56a25f59fbbb7c878e3767265be7bf8c74fae9278fd616e64fb9eed8a0a70b0" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.go.universal", + "confidence": "exact", + "anchors": [ + { + "file": "mux.go", + "startByte": 8002, + "endByte": 8006, + "startLine": 242, + "startColumn": 15, + "endLine": 242, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 5, + "label": "Mux" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "compass", + "graphDirected": true, + "owner": "Mux", + "state": "middlewares", + "file": "mux.go", + "declarationLine": 43, + "access": { + "method": "Use", + "methodLine": 100, + "line": 104, + "column": 2, + "expression": "mx.middlewares", + "operation": "write", + "text": "\tmx.middlewares = append(mx.middlewares, middlewares...)" + }, + "methodCandidates": [ + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "kind": "method", + "name": ".Use()", + "qualifiedName": "chi.Mux::Use", + "language": "go", + "source": { + "file": "mux.go", + "startByte": 3098, + "endByte": 3323, + "startLine": 100, + "startColumn": 0, + "endLine": 105, + "endColumn": 1 + }, + "details": { + "type": "symbol", + "data": { + "signature": "func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler)", + "signatureDigest": "659f46b12b16aad7608083957e6fae567b04419465f28ead26a3fed332042e5e", + "implementationDigest": "90b90e0dc2512b2eaa1dde6c97ce763901114da265768319579d335e0f808dd5", + "sourceDigest": "245aefc44e489ad148dd12f50ed3d4cc4c2906ac1f0a7f9910971c08d775e488" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.go.universal", + "confidence": "exact", + "anchors": [ + { + "file": "mux.go", + "startByte": 3113, + "endByte": 3116, + "startLine": 100, + "startColumn": 15, + "endLine": 100, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 0, + "label": "NewRouter" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "compass", + "graphDirected": true, + "owner": "Mux", + "state": "middlewares", + "file": "mux.go", + "declarationLine": 43, + "access": { + "method": "With", + "methodLine": 242, + "line": 252, + "column": 31, + "expression": "mx.middlewares", + "operation": "read", + "text": "\t\tmws = make(Middlewares, len(mx.middlewares))" + }, + "methodCandidates": [ + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "kind": "method", + "name": ".With()", + "qualifiedName": "chi.Mux::With", + "language": "go", + "source": { + "file": "mux.go", + "startByte": 7987, + "endByte": 8669, + "startLine": 242, + "startColumn": 0, + "endLine": 263, + "endColumn": 1 + }, + "details": { + "type": "symbol", + "data": { + "signature": "func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router", + "signatureDigest": "76785d02f71f43d41c2a72a89469f4245987421904054cfd01ea9d57b1df7446", + "implementationDigest": "67bd9b2192bc28c5f2e6fef637fa8ff9173a41dbe1406c1241cdaeae7b15cb37", + "sourceDigest": "c56a25f59fbbb7c878e3767265be7bf8c74fae9278fd616e64fb9eed8a0a70b0" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.go.universal", + "confidence": "exact", + "anchors": [ + { + "file": "mux.go", + "startByte": 8002, + "endByte": 8006, + "startLine": 242, + "startColumn": 15, + "endLine": 242, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 5, + "label": "Mux" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "graphify", + "graphDirected": false, + "owner": "Mux", + "state": "handler", + "file": "mux.go", + "declarationLine": 24, + "access": { + "method": "Use", + "methodLine": 100, + "line": 101, + "column": 5, + "expression": "mx.handler", + "operation": "read", + "text": "\tif mx.handler != nil {" + }, + "methodCandidates": [ + { + "id": "chi_mux_use", + "label": ".Use()", + "_origin": "ast", + "community": 3, + "file_type": "code", + "norm_label": ".use()", + "source_file": "mux.go", + "source_location": "L100" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "graphify", + "graphDirected": false, + "owner": "Mux", + "state": "handler", + "file": "mux.go", + "declarationLine": 24, + "access": { + "method": "With", + "methodLine": 242, + "line": 245, + "column": 19, + "expression": "mx.handler", + "operation": "read", + "text": "\tif !mx.inline && mx.handler == nil {" + }, + "methodCandidates": [ + { + "id": "chi_mux_with", + "label": ".With()", + "_origin": "ast", + "community": 3, + "file_type": "code", + "norm_label": ".with()", + "source_file": "mux.go", + "source_location": "L242" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "graphify", + "graphDirected": false, + "owner": "Mux", + "state": "middlewares", + "file": "mux.go", + "declarationLine": 43, + "access": { + "method": "Use", + "methodLine": 100, + "line": 104, + "column": 2, + "expression": "mx.middlewares", + "operation": "write", + "text": "\tmx.middlewares = append(mx.middlewares, middlewares...)" + }, + "methodCandidates": [ + { + "id": "chi_mux_use", + "label": ".Use()", + "_origin": "ast", + "community": 3, + "file_type": "code", + "norm_label": ".use()", + "source_file": "mux.go", + "source_location": "L100" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "chi", + "tool": "graphify", + "graphDirected": false, + "owner": "Mux", + "state": "middlewares", + "file": "mux.go", + "declarationLine": 43, + "access": { + "method": "With", + "methodLine": 242, + "line": 252, + "column": 31, + "expression": "mx.middlewares", + "operation": "read", + "text": "\t\tmws = make(Middlewares, len(mx.middlewares))" + }, + "methodCandidates": [ + { + "id": "chi_mux_with", + "label": ".With()", + "_origin": "ast", + "community": 3, + "file_type": "code", + "norm_label": ".with()", + "source_file": "mux.go", + "source_location": "L242" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "compass", + "graphDirected": true, + "owner": "_AtomicFile", + "state": "closed", + "file": "src/click/_compat.py", + "declarationLine": 460, + "access": { + "method": "__init__", + "methodLine": 456, + "line": 460, + "column": 9, + "expression": "self.closed", + "operation": "write", + "text": " self.closed = False" + }, + "methodCandidates": [ + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "kind": "method", + "name": ".__init__()", + "qualifiedName": "src.click._compat._AtomicFile::__init__", + "language": "python", + "source": { + "file": "src/click/_compat.py", + "startByte": 14270, + "endByte": 14486, + "startLine": 456, + "startColumn": 4, + "endLine": 460, + "endColumn": 27 + }, + "details": { + "type": "symbol", + "data": { + "signature": "def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None", + "signatureDigest": "d84173be446ee24b0a12066c2d90d512722920fecc637e3a1bb05a40a9b63a33", + "implementationDigest": "c97120b074ed32f1c7f0de7c7a565a07e2b80ed4bc6f37afd943d33aabb53526", + "sourceDigest": "b44d8fcfa6fc78850c1d26c3deabf0a5f5ed78498681b34002b2e8ea9bd13336" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.python.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/click/_compat.py", + "startByte": 14274, + "endByte": 14282, + "startLine": 456, + "startColumn": 8, + "endLine": 456, + "endColumn": 16 + } + ] + } + ], + "community": { + "id": 6, + "label": "_compat.py" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "compass", + "graphDirected": true, + "owner": "_AtomicFile", + "state": "closed", + "file": "src/click/_compat.py", + "declarationLine": 460, + "access": { + "method": "close", + "methodLine": 466, + "line": 467, + "column": 12, + "expression": "self.closed", + "operation": "read", + "text": " if self.closed:" + }, + "methodCandidates": [ + { + "id": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "kind": "method", + "name": ".close()", + "qualifiedName": "src.click._compat._AtomicFile::close", + "language": "python", + "source": { + "file": "src/click/_compat.py", + "startByte": 14569, + "endByte": 14769, + "startLine": 466, + "startColumn": 4, + "endLine": 471, + "endColumn": 26 + }, + "details": { + "type": "symbol", + "data": { + "signature": "def close(self, delete: bool = False) -> None", + "signatureDigest": "b4cbfe07542f1c9e9615bbbb74e8576072d57f76e4e67d8068501cb01829b335", + "implementationDigest": "57dc0e3d39394fa5b9d60ecfffc5549534ed5e32b5cbc67afbbd03ca67be4132", + "sourceDigest": "a0941ad915697320c72c55a4fc7a5e521681c08d9718bcddc5e1f6fa74e32443" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.python.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/click/_compat.py", + "startByte": 14573, + "endByte": 14578, + "startLine": 466, + "startColumn": 8, + "endLine": 466, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 6, + "label": "_compat.py" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "compass", + "graphDirected": true, + "owner": "_AtomicFile", + "state": "_real_filename", + "file": "src/click/_compat.py", + "declarationLine": 459, + "access": { + "method": "__init__", + "methodLine": 456, + "line": 459, + "column": 9, + "expression": "self._real_filename", + "operation": "write", + "text": " self._real_filename = real_filename" + }, + "methodCandidates": [ + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "kind": "method", + "name": ".__init__()", + "qualifiedName": "src.click._compat._AtomicFile::__init__", + "language": "python", + "source": { + "file": "src/click/_compat.py", + "startByte": 14270, + "endByte": 14486, + "startLine": 456, + "startColumn": 4, + "endLine": 460, + "endColumn": 27 + }, + "details": { + "type": "symbol", + "data": { + "signature": "def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None", + "signatureDigest": "d84173be446ee24b0a12066c2d90d512722920fecc637e3a1bb05a40a9b63a33", + "implementationDigest": "c97120b074ed32f1c7f0de7c7a565a07e2b80ed4bc6f37afd943d33aabb53526", + "sourceDigest": "b44d8fcfa6fc78850c1d26c3deabf0a5f5ed78498681b34002b2e8ea9bd13336" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.python.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/click/_compat.py", + "startByte": 14274, + "endByte": 14282, + "startLine": 456, + "startColumn": 8, + "endLine": 456, + "endColumn": 16 + } + ] + } + ], + "community": { + "id": 6, + "label": "_compat.py" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "compass", + "graphDirected": true, + "owner": "_AtomicFile", + "state": "_real_filename", + "file": "src/click/_compat.py", + "declarationLine": 459, + "access": { + "method": "name", + "methodLine": 463, + "line": 464, + "column": 16, + "expression": "self._real_filename", + "operation": "read", + "text": " return self._real_filename" + }, + "methodCandidates": [ + { + "id": "sha256:e878c6438bdce958c03a089d80b07294ed83ae9ee2f0f0a633df5fbf50ae9e52", + "kind": "method", + "name": ".name()", + "qualifiedName": "src.click._compat._AtomicFile::name", + "language": "python", + "source": { + "file": "src/click/_compat.py", + "startByte": 14506, + "endByte": 14563, + "startLine": 463, + "startColumn": 4, + "endLine": 464, + "endColumn": 34 + }, + "details": { + "type": "symbol", + "data": { + "signature": "def name(self) -> str", + "signatureDigest": "8a1643fce1c80fbaf299e8f34d0f11be952d7c2bcc90d418a3deccb866d100bb", + "implementationDigest": "3d6432f964f785a26f97a41f7ef7e63ff0a12a6ddbedb2a1076fd45d0b6ec4e9", + "sourceDigest": "e9954a4c6cda3a6ce002f3f95b71b4077db90e46f92dfd8c12a6f1539ec18f28" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.python.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/click/_compat.py", + "startByte": 14510, + "endByte": 14514, + "startLine": 463, + "startColumn": 8, + "endLine": 463, + "endColumn": 12 + } + ] + } + ], + "community": { + "id": 6, + "label": "_compat.py" + } + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "graphify", + "graphDirected": false, + "owner": "_AtomicFile", + "state": "closed", + "file": "src/click/_compat.py", + "declarationLine": 460, + "access": { + "method": "__init__", + "methodLine": 456, + "line": 460, + "column": 9, + "expression": "self.closed", + "operation": "write", + "text": " self.closed = False" + }, + "methodCandidates": [ + { + "id": "src_click_compat_atomicfile_init", + "label": ".__init__()", + "_callable": true, + "_origin": "ast", + "community": 24, + "file_type": "code", + "norm_label": ".__init__()", + "source_file": "src/click/_compat.py", + "source_location": "L456" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "graphify", + "graphDirected": false, + "owner": "_AtomicFile", + "state": "closed", + "file": "src/click/_compat.py", + "declarationLine": 460, + "access": { + "method": "close", + "methodLine": 466, + "line": 467, + "column": 12, + "expression": "self.closed", + "operation": "read", + "text": " if self.closed:" + }, + "methodCandidates": [ + { + "id": "src_click_compat_atomicfile_close", + "label": ".close()", + "_callable": true, + "_origin": "ast", + "community": 86, + "file_type": "code", + "norm_label": ".close()", + "source_file": "src/click/_compat.py", + "source_location": "L466" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "graphify", + "graphDirected": false, + "owner": "_AtomicFile", + "state": "_real_filename", + "file": "src/click/_compat.py", + "declarationLine": 459, + "access": { + "method": "__init__", + "methodLine": 456, + "line": 459, + "column": 9, + "expression": "self._real_filename", + "operation": "write", + "text": " self._real_filename = real_filename" + }, + "methodCandidates": [ + { + "id": "src_click_compat_atomicfile_init", + "label": ".__init__()", + "_callable": true, + "_origin": "ast", + "community": 24, + "file_type": "code", + "norm_label": ".__init__()", + "source_file": "src/click/_compat.py", + "source_location": "L456" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "click", + "tool": "graphify", + "graphDirected": false, + "owner": "_AtomicFile", + "state": "_real_filename", + "file": "src/click/_compat.py", + "declarationLine": 459, + "access": { + "method": "name", + "methodLine": 463, + "line": 464, + "column": 16, + "expression": "self._real_filename", + "operation": "read", + "text": " return self._real_filename" + }, + "methodCandidates": [ + { + "id": "src_click_compat_atomicfile_name", + "label": ".name()", + "_callable": true, + "_origin": "ast", + "community": 86, + "file_type": "code", + "norm_label": ".name()", + "source_file": "src/click/_compat.py", + "source_location": "L463" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "compass", + "graphDirected": true, + "owner": "Cleaner", + "state": "safelist", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 44, + "access": { + "method": "Cleaner", + "methodLine": 50, + "line": 52, + "column": 9, + "expression": "this.safelist", + "operation": "write", + "text": " this.safelist = safelist;" + }, + "methodCandidates": [ + { + "id": "sha256:66aaadb3479119cae227c0e51b5674eae36cb9b29e80428e2697aa7cb45518f0", + "kind": "constructor", + "name": "", + "qualifiedName": "org.jsoup.safety.Cleaner::", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1862, + "endByte": 1973, + "startLine": 50, + "startColumn": 4, + "endLine": 53, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "(Safelist)", + "signatureDigest": "badd69d3211600d45ddfbcf7377277a60ad61504ea3e020a22767d87fc96d6e8", + "implementationDigest": "f920ff6f090dca6d9bcd05476c7541243bb3b48f3e355822c2ebf5100aeb2cad", + "sourceDigest": "dbbac29bf7eb7e8bc862fd54e7e880df8ce1bcf29f4691bc63fba6ba9db20579" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1869, + "endByte": 1876, + "startLine": 50, + "startColumn": 11, + "endLine": 50, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 3, + "label": "Node" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:82d93a7cd4b92b3765fb539920bb5ca399119733f7cfc423203c4bebb16c1740", + "kind": "field", + "name": "safelist", + "qualifiedName": "org.jsoup.safety.Cleaner::safelist", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1705, + "endByte": 1713, + "startLine": 44, + "startColumn": 27, + "endLine": 44, + "endColumn": 35 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1705, + "endByte": 1713, + "startLine": 44, + "startColumn": 27, + "endLine": 44, + "endColumn": 35 + } + ] + } + ], + "community": { + "id": 15, + "label": "CleanerTest" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "compass", + "graphDirected": true, + "owner": "Cleaner", + "state": "safelist", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 44, + "access": { + "method": "createSafeElement", + "methodLine": 188, + "line": 197, + "column": 17, + "expression": "safelist", + "operation": "read", + "text": " if (safelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) { // will keep this attr" + }, + "methodCandidates": [ + { + "id": "sha256:7ce3db44fb899d894dd900ab8808bd0fdc411323a6276f4164fede39e46f8157", + "kind": "method", + "name": ".createSafeElement()", + "qualifiedName": "org.jsoup.safety.Cleaner::createSafeElement", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 8590, + "endByte": 11143, + "startLine": 188, + "startColumn": 4, + "endLine": 235, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "createSafeElement(Element)", + "signatureDigest": "99594481f6906a1e74b1aab1c5ce684e04ebbed4dbd02f7fe443ae0ebec1cef7", + "implementationDigest": "309927ce932885f2f766962666b1b592ba1df08c4f4d9ca0e2a520fc441f0761", + "sourceDigest": "e62cb7d5f499dc6cb58a7af0e341574806d9f9798b3b502e20ddadd9d7ec603e" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 8610, + "endByte": 8627, + "startLine": 188, + "startColumn": 24, + "endLine": 188, + "endColumn": 41 + } + ] + } + ], + "community": { + "id": 5, + "label": "Attributes" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:82d93a7cd4b92b3765fb539920bb5ca399119733f7cfc423203c4bebb16c1740", + "kind": "field", + "name": "safelist", + "qualifiedName": "org.jsoup.safety.Cleaner::safelist", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1705, + "endByte": 1713, + "startLine": 44, + "startColumn": 27, + "endLine": 44, + "endColumn": 35 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 1705, + "endByte": 1713, + "startLine": 44, + "startColumn": 27, + "endLine": 44, + "endColumn": 35 + } + ] + } + ], + "community": { + "id": 15, + "label": "CleanerTest" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "compass", + "graphDirected": true, + "owner": "CleaningVisitor", + "state": "destination", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 141, + "access": { + "method": "CleaningVisitor", + "methodLine": 143, + "line": 145, + "column": 13, + "expression": "this.destination", + "operation": "write", + "text": " this.destination = destination;" + }, + "methodCandidates": [ + { + "id": "sha256:d82578355e71b0d55f68499706f70276db9cf44a16bf283bba6360df410deec5", + "kind": "constructor", + "name": "", + "qualifiedName": "org.jsoup.safety.Cleaner::CleaningVisitor::", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6544, + "endByte": 6688, + "startLine": 143, + "startColumn": 8, + "endLine": 146, + "endColumn": 9 + }, + "details": { + "type": "symbol", + "data": { + "signature": "(Element,Element)", + "signatureDigest": "e8e92c45736788a17f106233dcb3198f7600101e4473e1fdf64b5f574c4e9780", + "implementationDigest": "6b2203f949da6991f90e660a94339fc571fcd632b7322801cc03a36a7d19109e", + "sourceDigest": "6c6a467c8a4ad1109775e3bef2e38139fab559e1c6a3ee4fb6d454739fb8efcc" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6552, + "endByte": 6567, + "startLine": 143, + "startColumn": 16, + "endLine": 143, + "endColumn": 31 + } + ] + } + ], + "community": { + "id": 10, + "label": "Element" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:b8452deb51561a6e6e1e8620c5ef9dcb5dae0f681633a87c34063a772975be8c", + "kind": "field", + "name": "destination", + "qualifiedName": "org.jsoup.safety.Cleaner::CleaningVisitor::destination", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6484, + "endByte": 6495, + "startLine": 141, + "startColumn": 24, + "endLine": 141, + "endColumn": 35 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6484, + "endByte": 6495, + "startLine": 141, + "startColumn": 24, + "endLine": 141, + "endColumn": 35 + } + ] + } + ], + "community": { + "id": 10, + "label": "Element" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "compass", + "graphDirected": true, + "owner": "CleaningVisitor", + "state": "destination", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 141, + "access": { + "method": "head", + "methodLine": 148, + "line": 158, + "column": 21, + "expression": "destination", + "operation": "write", + "text": " destination = destChild;" + }, + "methodCandidates": [ + { + "id": "sha256:65912eb3660f15092355589775d874a7a702efcf0336e9865cf93b4fb56aaa29", + "kind": "method", + "name": ".head()", + "qualifiedName": "org.jsoup.safety.Cleaner::CleaningVisitor::head", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6698, + "endByte": 8069, + "startLine": 148, + "startColumn": 8, + "endLine": 173, + "endColumn": 9 + }, + "details": { + "type": "symbol", + "data": { + "signature": "head(Node,int)", + "signatureDigest": "24041a46421d0170da7792402b63bfa06929d85ab9c7638648aabc3d7e6bc2bd", + "implementationDigest": "774186cc1e9706f6b22c053466513e970f55a68870e00ac677b1d2f6a4d4f75d", + "sourceDigest": "ff7dc01047765a1f468d43c800ac2f31c2ab0c608b8df791c0e4e35a134baa69" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6720, + "endByte": 6724, + "startLine": 148, + "startColumn": 30, + "endLine": 148, + "endColumn": 34 + } + ] + } + ], + "community": { + "id": 0, + "label": ".parse" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:b8452deb51561a6e6e1e8620c5ef9dcb5dae0f681633a87c34063a772975be8c", + "kind": "field", + "name": "destination", + "qualifiedName": "org.jsoup.safety.Cleaner::CleaningVisitor::destination", + "language": "java", + "source": { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6484, + "endByte": 6495, + "startLine": 141, + "startColumn": 24, + "endLine": 141, + "endColumn": 35 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startByte": 6484, + "endByte": 6495, + "startLine": 141, + "startColumn": 24, + "endLine": 141, + "endColumn": 35 + } + ] + } + ], + "community": { + "id": 10, + "label": "Element" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "graphify", + "graphDirected": false, + "owner": "Cleaner", + "state": "safelist", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 44, + "access": { + "method": "Cleaner", + "methodLine": 50, + "line": 52, + "column": 9, + "expression": "this.safelist", + "operation": "write", + "text": " this.safelist = safelist;" + }, + "methodCandidates": [ + { + "id": "src_main_java_org_jsoup_safety_cleaner_cleaner_cleaner", + "label": ".Cleaner()", + "_callable": true, + "_origin": "ast", + "community": 129, + "file_type": "code", + "norm_label": ".cleaner()", + "source_file": "src/main/java/org/jsoup/safety/Cleaner.java", + "source_location": "L50" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "graphify", + "graphDirected": false, + "owner": "Cleaner", + "state": "safelist", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 44, + "access": { + "method": "createSafeElement", + "methodLine": 188, + "line": 197, + "column": 17, + "expression": "safelist", + "operation": "read", + "text": " if (safelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) { // will keep this attr" + }, + "methodCandidates": [ + { + "id": "src_main_java_org_jsoup_safety_cleaner_cleaner_createsafeelement", + "label": ".createSafeElement()", + "_callable": true, + "_origin": "ast", + "community": 96, + "file_type": "code", + "metadata": { + "unresolved_calls": [ + { + "callee": "getHost", + "receiver_type": "URL", + "lang": "java", + "line": "L221" + } + ] + }, + "norm_label": ".createsafeelement()", + "source_file": "src/main/java/org/jsoup/safety/Cleaner.java", + "source_location": "L188" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "graphify", + "graphDirected": false, + "owner": "CleaningVisitor", + "state": "destination", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 141, + "access": { + "method": "CleaningVisitor", + "methodLine": 143, + "line": 145, + "column": 13, + "expression": "this.destination", + "operation": "write", + "text": " this.destination = destination;" + }, + "methodCandidates": [ + { + "id": "src_main_java_org_jsoup_safety_cleaner_cleaningvisitor_cleaningvisitor", + "label": ".CleaningVisitor()", + "_callable": true, + "_origin": "ast", + "community": 80, + "file_type": "code", + "norm_label": ".cleaningvisitor()", + "source_file": "src/main/java/org/jsoup/safety/Cleaner.java", + "source_location": "L143" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "jsoup", + "tool": "graphify", + "graphDirected": false, + "owner": "CleaningVisitor", + "state": "destination", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "declarationLine": 141, + "access": { + "method": "head", + "methodLine": 148, + "line": 158, + "column": 21, + "expression": "destination", + "operation": "write", + "text": " destination = destChild;" + }, + "methodCandidates": [ + { + "id": "src_main_java_org_jsoup_safety_cleaner_cleaningvisitor_head", + "label": ".head()", + "_callable": true, + "_origin": "ast", + "community": 80, + "file_type": "code", + "norm_label": ".head()", + "source_file": "src/main/java/org/jsoup/safety/Cleaner.java", + "source_location": "L148" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "compass", + "graphDirected": true, + "owner": "createStore", + "state": "currentState", + "file": "src/createStore.ts", + "declarationLine": 137, + "access": { + "method": "getState", + "methodLine": 166, + "line": 175, + "column": 12, + "expression": "currentState", + "operation": "read", + "text": " return currentState as S" + }, + "methodCandidates": [ + { + "id": "sha256:ac32c352aac7217b9b481c36ebdbd1304d84593968416c35058a0445860f30e2", + "kind": "function", + "name": "getState()", + "qualifiedName": "createStore.createStore.getState", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 5459, + "endByte": 5816, + "startLine": 166, + "startColumn": 2, + "endLine": 176, + "endColumn": 3 + }, + "details": { + "type": "symbol", + "data": { + "signature": "|params:|return:S" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 5468, + "endByte": 5476, + "startLine": 166, + "startColumn": 11, + "endLine": 166, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:f60e291b4921b2348cf32026034d2b812be220e0aca0765a380a634ce9799336", + "kind": "variable", + "name": "currentState", + "qualifiedName": "createStore.createStore.currentState", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 4579, + "endByte": 4591, + "startLine": 137, + "startColumn": 6, + "endLine": 137, + "endColumn": 18 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 4579, + "endByte": 4591, + "startLine": 137, + "startColumn": 6, + "endLine": 137, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "compass", + "graphDirected": true, + "owner": "createStore", + "state": "currentState", + "file": "src/createStore.ts", + "declarationLine": 137, + "access": { + "method": "dispatch", + "methodLine": 270, + "line": 299, + "column": 7, + "expression": "currentState", + "operation": "write", + "text": " currentState = currentReducer(currentState, action)" + }, + "methodCandidates": [ + { + "id": "sha256:ee27944e9b61e75b17b685fb67d51cccd459621e8ed9db30b18f5c7b02094612", + "kind": "function", + "name": "dispatch()", + "qualifiedName": "createStore.createStore.dispatch", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 9622, + "endByte": 11000, + "startLine": 270, + "startColumn": 2, + "endLine": 309, + "endColumn": 3 + }, + "details": { + "type": "symbol", + "data": { + "signature": "|params:A" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 9631, + "endByte": 9639, + "startLine": 270, + "startColumn": 11, + "endLine": 270, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:f60e291b4921b2348cf32026034d2b812be220e0aca0765a380a634ce9799336", + "kind": "variable", + "name": "currentState", + "qualifiedName": "createStore.createStore.currentState", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 4579, + "endByte": 4591, + "startLine": 137, + "startColumn": 6, + "endLine": 137, + "endColumn": 18 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 4579, + "endByte": 4591, + "startLine": 137, + "startColumn": 6, + "endLine": 137, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "compass", + "graphDirected": true, + "owner": "createStore", + "state": "isDispatching", + "file": "src/createStore.ts", + "declarationLine": 143, + "access": { + "method": "getState", + "methodLine": 166, + "line": 167, + "column": 9, + "expression": "isDispatching", + "operation": "read", + "text": " if (isDispatching) {" + }, + "methodCandidates": [ + { + "id": "sha256:ac32c352aac7217b9b481c36ebdbd1304d84593968416c35058a0445860f30e2", + "kind": "function", + "name": "getState()", + "qualifiedName": "createStore.createStore.getState", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 5459, + "endByte": 5816, + "startLine": 166, + "startColumn": 2, + "endLine": 176, + "endColumn": 3 + }, + "details": { + "type": "symbol", + "data": { + "signature": "|params:|return:S" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 5468, + "endByte": 5476, + "startLine": 166, + "startColumn": 11, + "endLine": 166, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:e624ded2e63d3070d23b0928b66dd153fcd656a4155b1d5522c6560b8dca5c38", + "kind": "variable", + "name": "isDispatching", + "qualifiedName": "createStore.createStore.isDispatching", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 4827, + "endByte": 4840, + "startLine": 143, + "startColumn": 6, + "endLine": 143, + "endColumn": 19 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 4827, + "endByte": 4840, + "startLine": 143, + "startColumn": 6, + "endLine": 143, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "compass", + "graphDirected": true, + "owner": "createStore", + "state": "isDispatching", + "file": "src/createStore.ts", + "declarationLine": 143, + "access": { + "method": "dispatch", + "methodLine": 270, + "line": 298, + "column": 7, + "expression": "isDispatching", + "operation": "write", + "text": " isDispatching = true" + }, + "methodCandidates": [ + { + "id": "sha256:ee27944e9b61e75b17b685fb67d51cccd459621e8ed9db30b18f5c7b02094612", + "kind": "function", + "name": "dispatch()", + "qualifiedName": "createStore.createStore.dispatch", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 9622, + "endByte": 11000, + "startLine": 270, + "startColumn": 2, + "endLine": 309, + "endColumn": 3 + }, + "details": { + "type": "symbol", + "data": { + "signature": "|params:A" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 9631, + "endByte": 9639, + "startLine": 270, + "startColumn": 11, + "endLine": 270, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:e624ded2e63d3070d23b0928b66dd153fcd656a4155b1d5522c6560b8dca5c38", + "kind": "variable", + "name": "isDispatching", + "qualifiedName": "createStore.createStore.isDispatching", + "language": "typescript", + "source": { + "file": "src/createStore.ts", + "startByte": 4827, + "endByte": 4840, + "startLine": 143, + "startColumn": 6, + "endLine": 143, + "endColumn": 19 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.typescript.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/createStore.ts", + "startByte": 4827, + "endByte": 4840, + "startLine": 143, + "startColumn": 6, + "endLine": 143, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 15, + "label": "createStore (src/createStore.ts:L86)" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "graphify", + "graphDirected": false, + "owner": "createStore", + "state": "currentState", + "file": "src/createStore.ts", + "declarationLine": 137, + "access": { + "method": "getState", + "methodLine": 166, + "line": 175, + "column": 12, + "expression": "currentState", + "operation": "read", + "text": " return currentState as S" + }, + "methodCandidates": [ + { + "id": "src_createstore_createstore_getstate", + "label": "getState()", + "_callable": true, + "_origin": "ast", + "community": 25, + "file_type": "code", + "norm_label": "getstate()", + "source_file": "src/createStore.ts", + "source_location": "L166" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "graphify", + "graphDirected": false, + "owner": "createStore", + "state": "currentState", + "file": "src/createStore.ts", + "declarationLine": 137, + "access": { + "method": "dispatch", + "methodLine": 270, + "line": 299, + "column": 7, + "expression": "currentState", + "operation": "write", + "text": " currentState = currentReducer(currentState, action)" + }, + "methodCandidates": [ + { + "id": "src_createstore_createstore_dispatch", + "label": "dispatch()", + "_callable": true, + "_origin": "ast", + "community": 25, + "file_type": "code", + "norm_label": "dispatch()", + "source_file": "src/createStore.ts", + "source_location": "L270" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "graphify", + "graphDirected": false, + "owner": "createStore", + "state": "isDispatching", + "file": "src/createStore.ts", + "declarationLine": 143, + "access": { + "method": "getState", + "methodLine": 166, + "line": 167, + "column": 9, + "expression": "isDispatching", + "operation": "read", + "text": " if (isDispatching) {" + }, + "methodCandidates": [ + { + "id": "src_createstore_createstore_getstate", + "label": "getState()", + "_callable": true, + "_origin": "ast", + "community": 25, + "file_type": "code", + "norm_label": "getstate()", + "source_file": "src/createStore.ts", + "source_location": "L166" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "redux", + "tool": "graphify", + "graphDirected": false, + "owner": "createStore", + "state": "isDispatching", + "file": "src/createStore.ts", + "declarationLine": 143, + "access": { + "method": "dispatch", + "methodLine": 270, + "line": 298, + "column": 7, + "expression": "isDispatching", + "operation": "write", + "text": " isDispatching = true" + }, + "methodCandidates": [ + { + "id": "src_createstore_createstore_dispatch", + "label": "dispatch()", + "_callable": true, + "_origin": "ast", + "community": 25, + "file_type": "code", + "norm_label": "dispatch()", + "source_file": "src/createStore.ts", + "source_location": "L270" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "compass", + "graphDirected": true, + "owner": "IntoIter", + "state": "deferred_dirs", + "file": "src/lib.rs", + "declarationLine": 598, + "access": { + "method": "handle_entry", + "methodLine": 840, + "line": 875, + "column": 13, + "expression": "self.deferred_dirs", + "operation": "receiver", + "text": " self.deferred_dirs.push(dent);" + }, + "methodCandidates": [ + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "kind": "method", + "name": ".handle_entry()", + "qualifiedName": "walkdir::IntoIter::handle_entry", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 29143, + "endByte": 30869, + "startLine": 840, + "startColumn": 4, + "endLine": 882, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "fn handle_entry( &mut self, mut dent: DirEntry, ) -> Option>", + "signatureDigest": "310808d8882a85c24aded6c0eb9116d92403a8056cef0c155e27044ca844eff3", + "implementationDigest": "97e2e9dfc3833955d8153f6420bf6cc8aaaa10f2517086fda9d91f68f4c1c173", + "sourceDigest": "70d33c8bd4eae5883f1a1af831cd18d5caa5711bc2cc33a8c14f851f7305768b" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 29146, + "endByte": 29158, + "startLine": 840, + "startColumn": 7, + "endLine": 840, + "endColumn": 19 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:413c3c0dfae5a89e15e2e094ec8961c9ef4acfb4c7a56fb06f4d6943734862c1", + "kind": "field", + "name": "deferred_dirs", + "qualifiedName": "walkdir::IntoIter::deferred_dirs", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 20224, + "endByte": 20237, + "startLine": 598, + "startColumn": 4, + "endLine": 598, + "endColumn": 17 + }, + "details": { + "type": "symbol", + "data": { + "signature": "deferred_dirs: Vec", + "signatureDigest": "37906d395875d925db71ec7a0bd3e75893f147caf02be49839f7147d6668a281", + "sourceDigest": "6ee20049871a0b0aa99d93b490c4579d2909a7e7f9cbd415778b8e2f0cdf9d86" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 20224, + "endByte": 20237, + "startLine": 598, + "startColumn": 4, + "endLine": 598, + "endColumn": 17 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "compass", + "graphDirected": true, + "owner": "IntoIter", + "state": "deferred_dirs", + "file": "src/lib.rs", + "declarationLine": 598, + "access": { + "method": "get_deferred_dir", + "methodLine": 884, + "line": 886, + "column": 29, + "expression": "self.deferred_dirs", + "operation": "read", + "text": " if self.depth < self.deferred_dirs.len() {" + }, + "methodCandidates": [ + { + "id": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "kind": "method", + "name": ".get_deferred_dir()", + "qualifiedName": "walkdir::IntoIter::get_deferred_dir", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 30875, + "endByte": 31482, + "startLine": 884, + "startColumn": 4, + "endLine": 899, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "fn get_deferred_dir(&mut self) -> Option", + "signatureDigest": "7fed725f8244f07e0a984c8f87df3675c999315adb2015ff9fbd2e1e0569d6ba", + "implementationDigest": "0a593159692ab4c497de9873715dbd786d66320b28ce7cc0b47189042ea48b6a", + "sourceDigest": "2b7edf004c0eb7c3996b3b1d045dc30e1251559b0352fec36bc53304160f334e" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 30878, + "endByte": 30894, + "startLine": 884, + "startColumn": 7, + "endLine": 884, + "endColumn": 23 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:413c3c0dfae5a89e15e2e094ec8961c9ef4acfb4c7a56fb06f4d6943734862c1", + "kind": "field", + "name": "deferred_dirs", + "qualifiedName": "walkdir::IntoIter::deferred_dirs", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 20224, + "endByte": 20237, + "startLine": 598, + "startColumn": 4, + "endLine": 598, + "endColumn": 17 + }, + "details": { + "type": "symbol", + "data": { + "signature": "deferred_dirs: Vec", + "signatureDigest": "37906d395875d925db71ec7a0bd3e75893f147caf02be49839f7147d6668a281", + "sourceDigest": "6ee20049871a0b0aa99d93b490c4579d2909a7e7f9cbd415778b8e2f0cdf9d86" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 20224, + "endByte": 20237, + "startLine": 598, + "startColumn": 4, + "endLine": 598, + "endColumn": 17 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "compass", + "graphDirected": true, + "owner": "IntoIter", + "state": "oldest_opened", + "file": "src/lib.rs", + "declarationLine": 591, + "access": { + "method": "push", + "methodLine": 901, + "line": 945, + "column": 13, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = self.oldest_opened.checked_add(1).unwrap();" + }, + "methodCandidates": [ + { + "id": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "kind": "method", + "name": ".push()", + "qualifiedName": "walkdir::IntoIter::push", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 31488, + "endByte": 33947, + "startLine": 901, + "startColumn": 4, + "endLine": 948, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "fn push(&mut self, dent: &DirEntry) -> Result<()>", + "signatureDigest": "cb10b4c8df4099b3ca4e1436d2b521ca35a83bbaf1fc95b74e90a582ed0d4b51", + "implementationDigest": "e8e95f68d459cb51b8ba9e86b13def3c4a5324058be52b2ff950c1352c0aa048", + "sourceDigest": "14aa7a18f314d62746ec2e5b1d3b947ba4f4e270760b4224db5e127645e9faff" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 31491, + "endByte": 31495, + "startLine": 901, + "startColumn": 7, + "endLine": 901, + "endColumn": 11 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:b127352da415182e7b8716381b845e7ae6093fe6b79ceb9e0a48ebbffaeae1d3", + "kind": "field", + "name": "oldest_opened", + "qualifiedName": "walkdir::IntoIter::oldest_opened", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 19882, + "endByte": 19895, + "startLine": 591, + "startColumn": 4, + "endLine": 591, + "endColumn": 17 + }, + "details": { + "type": "symbol", + "data": { + "signature": "oldest_opened: usize", + "signatureDigest": "b21b147403ccbf752075dc8deafc452ca756f8a458d6b87ebd58bd769acf4bbb", + "sourceDigest": "e21fc19d3741ff510e45f6238b1be153c24fb3899c0896a7ee0960a690f8c319" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 19882, + "endByte": 19895, + "startLine": 591, + "startColumn": 4, + "endLine": 591, + "endColumn": 17 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "compass", + "graphDirected": true, + "owner": "IntoIter", + "state": "oldest_opened", + "file": "src/lib.rs", + "declarationLine": 591, + "access": { + "method": "pop", + "methodLine": 950, + "line": 958, + "column": 9, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = min(self.oldest_opened, self.stack_list.len());" + }, + "methodCandidates": [ + { + "id": "sha256:3d843599c82e0cf88d7512278cc2b4f2ffc87c5797785e0c9caad89ab4af8f98", + "kind": "method", + "name": ".pop()", + "qualifiedName": "walkdir::IntoIter::pop", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 33953, + "endByte": 34437, + "startLine": 950, + "startColumn": 4, + "endLine": 959, + "endColumn": 5 + }, + "details": { + "type": "symbol", + "data": { + "signature": "fn pop(&mut self)", + "signatureDigest": "0fa51313f817eed32447ebd07cb6dcda1265a55558a49a35712231b89d0f8c25", + "implementationDigest": "55581b03dfee9d578e5c5cc50db986a587f795e0f0bd1cf4b60769627bba0549", + "sourceDigest": "1553775e6184fbdc67a0491e65098678af0c0dad5631134e7737adeef7d5bd70" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 33956, + "endByte": 33959, + "startLine": 950, + "startColumn": 7, + "endLine": 950, + "endColumn": 10 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "stateCandidates": [ + { + "id": "sha256:b127352da415182e7b8716381b845e7ae6093fe6b79ceb9e0a48ebbffaeae1d3", + "kind": "field", + "name": "oldest_opened", + "qualifiedName": "walkdir::IntoIter::oldest_opened", + "language": "rust", + "source": { + "file": "src/lib.rs", + "startByte": 19882, + "endByte": 19895, + "startLine": 591, + "startColumn": 4, + "endLine": 591, + "endColumn": 17 + }, + "details": { + "type": "symbol", + "data": { + "signature": "oldest_opened: usize", + "signatureDigest": "b21b147403ccbf752075dc8deafc452ca756f8a458d6b87ebd58bd769acf4bbb", + "sourceDigest": "e21fc19d3741ff510e45f6238b1be153c24fb3899c0896a7ee0960a690f8c319" + } + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.rust.universal", + "confidence": "exact", + "anchors": [ + { + "file": "src/lib.rs", + "startByte": 19882, + "endByte": 19895, + "startLine": 591, + "startColumn": 4, + "endLine": 591, + "endColumn": 17 + } + ] + } + ], + "community": { + "id": 0, + "label": "DirEntry" + } + } + ], + "connectingRecords": [], + "status": "no_contact_edge", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "graphify", + "graphDirected": false, + "owner": "IntoIter", + "state": "deferred_dirs", + "file": "src/lib.rs", + "declarationLine": 598, + "access": { + "method": "handle_entry", + "methodLine": 840, + "line": 875, + "column": 13, + "expression": "self.deferred_dirs", + "operation": "receiver", + "text": " self.deferred_dirs.push(dent);" + }, + "methodCandidates": [ + { + "id": "src_lib_intoiter_handle_entry", + "label": ".handle_entry()", + "_origin": "ast", + "community": 2, + "file_type": "code", + "norm_label": ".handle_entry()", + "source_file": "src/lib.rs", + "source_location": "L840" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "graphify", + "graphDirected": false, + "owner": "IntoIter", + "state": "deferred_dirs", + "file": "src/lib.rs", + "declarationLine": 598, + "access": { + "method": "get_deferred_dir", + "methodLine": 884, + "line": 886, + "column": 29, + "expression": "self.deferred_dirs", + "operation": "read", + "text": " if self.depth < self.deferred_dirs.len() {" + }, + "methodCandidates": [ + { + "id": "src_lib_intoiter_get_deferred_dir", + "label": ".get_deferred_dir()", + "_origin": "ast", + "community": 2, + "file_type": "code", + "norm_label": ".get_deferred_dir()", + "source_file": "src/lib.rs", + "source_location": "L884" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "graphify", + "graphDirected": false, + "owner": "IntoIter", + "state": "oldest_opened", + "file": "src/lib.rs", + "declarationLine": 591, + "access": { + "method": "push", + "methodLine": 901, + "line": 945, + "column": 13, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = self.oldest_opened.checked_add(1).unwrap();" + }, + "methodCandidates": [ + { + "id": "src_lib_intoiter_push", + "label": ".push()", + "_origin": "ast", + "community": 2, + "file_type": "code", + "norm_label": ".push()", + "source_file": "src/lib.rs", + "source_location": "L901" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + }, + { + "repository": "walkdir", + "tool": "graphify", + "graphDirected": false, + "owner": "IntoIter", + "state": "oldest_opened", + "file": "src/lib.rs", + "declarationLine": 591, + "access": { + "method": "pop", + "methodLine": 950, + "line": 958, + "column": 9, + "expression": "self.oldest_opened", + "operation": "write", + "text": " self.oldest_opened = min(self.oldest_opened, self.stack_list.len());" + }, + "methodCandidates": [ + { + "id": "src_lib_intoiter_pop", + "label": ".pop()", + "_origin": "ast", + "community": 2, + "file_type": "code", + "norm_label": ".pop()", + "source_file": "src/lib.rs", + "source_location": "L950" + } + ], + "stateCandidates": [], + "connectingRecords": [], + "status": "missing_state", + "contactSupported": false, + "selectedLineSupported": false + } + ] +} diff --git a/benchmarks/agent_query/tests/test_state_access.py b/benchmarks/agent_query/tests/test_state_access.py new file mode 100644 index 000000000..bd6716ff9 --- /dev/null +++ b/benchmarks/agent_query/tests/test_state_access.py @@ -0,0 +1,111 @@ +import copy +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.state_access_audit import anchor, assess, candidates, read + + +class StateAccessTests(unittest.TestCase): + def setUp(self): + self.case = dict(repository='fixture', file='state.py') + self.group = dict(owner='Box', state='value', declarationLine=2) + self.access = dict(method='get', methodLine=3, line=4) + self.graph = dict(directed=True, nodes=[ + dict(id='m', name='.get()', kind='method', source=dict(file='state.py', startLine=3)), + dict(id='s', name='value', kind='field', source=dict(file='state.py', startLine=2))], links=[ + dict(id='e', source='m', target='s', kind='references', + relationshipSite=dict(file='state.py', startLine=4))]) + + def score(self, graph=None, tool='compass'): + return assess(graph or self.graph, tool, self.case, self.group, self.access) + + def test_contact_and_selected_occurrence_are_separate(self): + row = self.score() + self.assertTrue(row['contactSupported']) + self.assertTrue(row['selectedLineSupported']) + self.graph['links'][0]['relationshipSite']['startLine'] = 5 + row = self.score() + self.assertTrue(row['contactSupported']) + self.assertFalse(row['selectedLineSupported']) + + def test_calls_containment_and_reverse_edges_are_not_contacts(self): + for kind in ('calls', 'contains', 'returns', 'instantiates'): + with self.subTest(kind=kind): + self.graph['links'][0]['kind'] = kind + self.assertFalse(self.score()['contactSupported']) + self.graph['links'][0].update(kind='references', source='s', target='m') + row = self.score() + self.assertEqual(len(row['connectingRecords']), 1) + self.assertFalse(row['contactSupported']) + + def test_missing_state_is_distinct_from_missing_edge(self): + self.graph['links'].clear() + self.assertEqual(self.score()['status'], 'no_contact_edge') + self.graph['nodes'].pop() + self.assertEqual(self.score()['status'], 'missing_state') + + def test_duplicate_candidates_are_never_silently_selected(self): + for index, status in [(0, 'ambiguous_callable'), (1, 'ambiguous_state')]: + graph = copy.deepcopy(self.graph) + node = dict(graph['nodes'][index], id='duplicate') + graph['nodes'].append(node) + row = self.score(graph) + self.assertEqual(row['status'], status) + self.assertFalse(row['contactSupported']) + + def test_shadow_parameter_and_other_file_do_not_replace_field(self): + self.graph['nodes'][1].update(kind='parameter', source=dict(file='state.py', startLine=3)) + self.assertEqual(self.score()['status'], 'missing_state') + self.graph['nodes'][1]['source'] = dict(file='other.py', startLine=2) + self.assertEqual(self.score()['status'], 'missing_state') + + def test_constructor_uses_exact_source_identity(self): + node = dict(id='ctor', kind='constructor', name='', source=dict(file='Box.java', startLine=8)) + self.assertEqual(candidates([node], 'compass', 'Box.java', 8, 'Box', constructor=True), [node]) + self.assertEqual(candidates([node], 'compass', 'Box.java', 9, 'Box', constructor=True), []) + self.assertEqual(candidates([node], 'compass', 'Box.java', 8, 'Box'), []) + + def test_parallel_occurrences_retained_and_order_stable(self): + self.graph['links'].append(dict(self.graph['links'][0], id='other')) + row = self.score() + self.assertEqual(len(row['connectingRecords']), 2) + self.graph['links'].reverse() + self.graph['nodes'].reverse() + self.assertEqual(self.score(), row) + + def test_graphify_representation_and_undirected_flag_preserved(self): + graph = dict(directed=False, nodes=[ + dict(id='m', label='.get()', source_file='state.py', source_location='L3'), + dict(id='s', label='value', source_file='state.py', source_location='L2')], links=[ + dict(source='m', target='s', relation='references', source_file='state.py', source_location='L4')]) + row = self.score(graph, 'graphify') + self.assertTrue(row['selectedLineSupported']) + self.assertFalse(row['graphDirected']) + # Contact here is stored endpoint order, not a directed graph path. + graph['links'][0]['source_location'] = 'unknown' + self.assertFalse(self.score(graph, 'graphify')['selectedLineSupported']) + + def test_missing_edge_occurrence_cannot_use_declaration_evidence(self): + edge = self.graph['links'][0] + del edge['relationshipSite'] + edge['evidence'] = [dict(anchors=[dict(file='state.py', startLine=4)])] + self.assertTrue(self.score()['contactSupported']) + self.assertFalse(self.score()['selectedLineSupported']) + + def test_bounded_reader_fails_instead_of_truncating(self): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / 'input' + path.write_bytes(b'1234') + self.assertEqual(read(path, 4), b'1234') + with self.assertRaises(ValueError): + read(path, 3) + + def test_graphify_anchor_parser_does_not_guess(self): + self.assertEqual(anchor(dict(source_file='a', source_location='L42-L50'), 'graphify'), ('a', 42)) + for location in ('line 42', 'L0', 'L42 trailing', ''): + self.assertEqual(anchor(dict(source_file='a', source_location=location), 'graphify'), ('a', None)) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index d91ad2e17..dcc20e353 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2706,6 +2706,87 @@ artifacts under `exact-symbol-01` through `exact-symbol-08`. Source selection, responsibility synthesis, actual god-object defect evidence, broader edge precision, longer walks and fresh confirmation remain unfinished. +## Shared-state evidence prerequisite for cohesion analysis + +Registration `1c6f2a43` freezes 20 source-selected access sites: two state slots, +each used by two methods, in each of the existing five development repositories. +The subjects and earlier graph inventories were already known; this is not a +blind or held-out evaluation. Java includes `Cleaner.CleaningVisitor`, and Redux +uses closure variables rather than class fields. These are prerequisites for +state-sharing analysis, not equivalent whole-class cohesion samples. + +The diagnostic scans the same complete, hash-pinned frozen graphs for both +tools, with a 512 MiB bound per graph. It identifies endpoints by exact source +file, declaration line and symbol, retaining ambiguity. It separately checks +method-to-state contact records and occurrence provenance at the selected access +line. Calls on a field's type, containment, owner-type references and excerpts do +not count as state-access edges. It does not measure public query retrieval. + +| Subject | Compass state slots represented | Graphify state slots represented | Compass access sites | Graphify access sites | +| --- | ---: | ---: | ---: | ---: | +| Chi / Go | 0/2 | 0/2 | 0/4 | 0/4 | +| Click / Python | 0/2 | 0/2 | 0/4 | 0/4 | +| jsoup / Java | 2/2 | 0/2 | 0/4 | 0/4 | +| Redux / TypeScript | 2/2 | 0/2 | 0/4 | 0/4 | +| WalkDir / Rust | 2/2 | 0/2 | 0/4 | 0/4 | +| **Total** | **6/10** | **0/10** | **0/20** | **0/20** | + +All 20 accessing callable coordinates identify one node in both tools. Compass +has missing state endpoints at eight access sites and represented endpoints +with no connecting records at the other twelve. Graphify has no state endpoint +at any of the ten pinned declaration/introduction coordinates. There are no +connecting records of any kind or direction between any candidate endpoints; +the zero result is not caused by the diagnostic's relation whitelist. The +Graphify containers declare `directed: false`; saved endpoint order must not be +interpreted as native directed path support. The report preserves that flag. + +These results contradict using absent method/state links as evidence of low +cohesion in these subjects. They do not show independent responsibilities, +a god-object defect, poor whole-class cohesion, or overall tool superiority. +The extra six Compass declarations do not supply the missing access evidence. +Python's state coordinates are first assignments to instance attributes, so +those rows specifically test whether the graph represents those introductions. + +Code inspection locates concrete producer gaps at baseline `7aef6a0c`: + +- `walk_rust_evidence` in `compass-languages/src/evidence/build.rs` emits calls, + macro invocations and declaration references, but has no field-expression + access emission arm. The selected Rust field declarations already exist. +- `walk_java_evidence` in that module emits calls, construction, annotations and + type relationships, but has no ordinary field-access emission arm. Both + selected Java field declarations already exist. +- TypeScript identifier traversal calls `emit_callable_reference`; that function + explicitly skips local declarations without proven callable status. The two + Redux closure variables are declared but their ordinary value uses are lost. + Broadening that code requires a truthful value-reference contract, not + relabeling arbitrary state as callable. + +The existing member-access candidate projects to a `references` edge with +`member-access` context, so qualified field-access evidence can use an existing +relationship representation. The next production work belongs in language +fact emission and qualified resolution, with shadowing/ambiguity negatives, +precise occurrence anchors, bounded lookup, cache invalidation and affected +language qualification. Hub ranking cannot reconstruct these missing facts. +No producer, capability, runtime behavior or release version changes in this +checkpoint; the gaps remain open. + +`state_access_audit.py` replays source pins, clean checkout state, exact source +witnesses, graph hashes, candidate sets, all connecting records and its own +script hash. The committed registration and review live under +`benchmarks/agent_query/`; raw development artifacts and logs are under +`state-access-01`. The first collector attempt incorrectly required a directed +container and stopped on Graphify's undirected container; retaining that flag +instead permitted the registered stored-endpoint diagnostic. No partial result +was scored. Before registration, source-coordinate assertions also caught and +corrected off-by-one Redux/Rust anchors. + +The eleven focused auditor tests pass, including positive contact evidence, +shadowed targets, duplicate candidates, wrong relations/directions, occurrence +mismatch, constructor spelling, parallel records and bounded reads. The complete benchmark suite passes 148 tests. The saved +real-repository review replays byte-for-byte. No Rust/JavaScript tests or +extraction gates were rerun for this benchmark/documentation-only checkpoint; +previous production validation remains tied to its earlier commit. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 4a1be2650e9b79e9534c6a1004d3e221deb81b13 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:45:31 -0700 Subject: [PATCH 77/97] Register Rust state-access correction and fixed-protocol replay --- ...st_state_access_development_registration.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 benchmarks/agent_query/rust_state_access_development_registration.json diff --git a/benchmarks/agent_query/rust_state_access_development_registration.json b/benchmarks/agent_query/rust_state_access_development_registration.json new file mode 100644 index 000000000..9f7e2cd19 --- /dev/null +++ b/benchmarks/agent_query/rust_state_access_development_registration.json @@ -0,0 +1,16 @@ +{ + "schema": "compass.rust-state-access-development-registration/1", + "baselineCommit": "9cdeab6a1b0ba1d75179453145dff0b2b48e0aa7", + "sourceRegistration": "benchmarks/agent_query/state_access_development_registration.json", + "sourceRegistrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "scope": "Fix missing Rust field-access evidence discovered on known development subjects. Existing Members capability and member-access representation; no new language cutover, read/write classification, LCOM or god-object diagnosis.", + "productContract": "Emit exact field occurrence candidates for source-proven nominal receivers. Restrict targets to fields; resolve through existing qualified universal lookup. Preserve unsupported and shadowed receivers as unresolved. Skip method-selector syntax while retaining its receiver field accesses. Reuse bounded source-type inference; do not invent external field nodes or choose a same-named field.", + "evaluation": "Rebuild Compass graphs from the same five pinned read-only repositories. Reuse the unchanged frozen Graphify graphs. Replay all 20 registered state sites with the same full-graph auditor and retain all original results. Record full graph deltas and unchanged/non-Rust controls; report any changed existing records/community assignments, not just recovered sites. This is not a build-time comparison, independent confirmation or public-query benchmark.", + "verification": [ + "Fail-before native field-access regression and negative receiver/scope tests", + "Source-anchor, multiplicity, deterministic resolution and cross-file qualification regressions", + "AST cache invalidation test and native baseline", + "Production fixture qualification", + "Pinned source/graph/binary hashes; replayable 20-site comparison and graph deltas" + ] +} From 56d7d559fa3bc73a9dc006ac886814ca300669f4 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:53:31 -0700 Subject: [PATCH 78/97] Register paired public retrieval control for Rust state contacts --- .../rust_state_access_development_registration.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/benchmarks/agent_query/rust_state_access_development_registration.json b/benchmarks/agent_query/rust_state_access_development_registration.json index 9f7e2cd19..efea0acef 100644 --- a/benchmarks/agent_query/rust_state_access_development_registration.json +++ b/benchmarks/agent_query/rust_state_access_development_registration.json @@ -12,5 +12,15 @@ "AST cache invalidation test and native baseline", "Production fixture qualification", "Pinned source/graph/binary hashes; replayable 20-site comparison and graph deltas" - ] + ], + "publicFollowup": { + "scope": "Known-ID-assisted retrieval control, not natural-language discovery or identity resolution. Registered before rebuilt graphs or follow-up outputs are inspected.", + "tasks": [ + "WalkDir IntoIter::handle_entry -> deferred_dirs at src/lib.rs:875", + "WalkDir IntoIter::get_deferred_dir -> deferred_dirs at src/lib.rs:886", + "WalkDir IntoIter::push -> oldest_opened at src/lib.rs:945", + "WalkDir IntoIter::pop -> oldest_opened at src/lib.rs:958" + ], + "policy": "For each source-validated callable, provide each tool its own exact graph ID as an explicit task input and issue one public MCP get_neighbors call with no relation filter. Preserve native defaults, all errors, truncation and response bytes; no retries or source follow-ups. Score field-neighbor identity separately from occurrence anchoring and report graph consistency, not authored explanations. Use unchanged baseline Graphify graphs and new Compass graphs." + } } From 390406c176f6116bcd1a8f64feee468c9e876497 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 04:57:59 -0700 Subject: [PATCH 79/97] Emit qualified Rust field-access evidence with exact occurrences --- .github/workflows/compass-ci.yml | 3 + CHANGELOG.md | 5 + COMPATIBILITY.md | 16 ++ MIGRATION.md | 5 + crates/compass-files/src/cache.rs | 2 +- crates/compass-files/tests/contracts.rs | 6 + .../compass-languages/src/evidence/build.rs | 72 ++++++++- .../tests/rust_field_access.rs | 128 ++++++++++++++++ .../tests/rust_field_access.rs | 145 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 2 +- docs/reference/universal-semantic-evidence.md | 12 ++ 11 files changed, 392 insertions(+), 4 deletions(-) create mode 100644 crates/compass-languages/tests/rust_field_access.rs create mode 100644 crates/compass-resolve/tests/rust_field_access.rs diff --git a/.github/workflows/compass-ci.yml b/.github/workflows/compass-ci.yml index 1a8fbbafe..a13693c74 100644 --- a/.github/workflows/compass-ci.yml +++ b/.github/workflows/compass-ci.yml @@ -110,6 +110,9 @@ jobs: cargo test -p compass-graph --test analyze_coverage --locked cargo test -p compass-query --test bounded_path_oracle --test code_traversal --test coverage_paths --locked + - name: Rust state-access evidence contracts + run: cargo test -p compass-languages -p compass-resolve --test rust_field_access --locked + - name: Comparison scorer regressions (no competitor installation) run: python3 -m unittest discover -s benchmarks/agent_query/tests diff --git a/CHANGELOG.md b/CHANGELOG.md index b12bbec36..88055476e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Emit Rust field-access evidence for source-proven nominal receivers, preserving + exact occurrences and unknown or shadowed receiver outcomes. Publish qualified + field contacts as references without inventing read/write effects. Rebuild + graphs to refresh older AST caches. + - Add `search --exact` and MCP `search_symbols` exact mode with optional source file, declaration line and node-kind filters. Return all bounded exact matches without lexical fallback; preserve ambiguity and incomplete lookup. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 3456e4f2e..cf6204dcd 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -31,6 +31,22 @@ not maintain command-specific fallbacks for older releases. Compass 0.3.0 itself remains supported. The extension adapts typed call-query results for the known nested-anchor limitation in that stable release. +## Rust field-access evidence + +Rust extraction emits member-access occurrences for explicit field expressions, +including fields in method receivers and scalar-indexed receiver chains when +bounded source-type evidence establishes the nominal owner. Existing universal +resolution selects only field declarations, preserving parallel occurrences. +Graph v1 publishes these as `references` with member-access provenance, not as +read/write-effect or runtime-alias proofs. Method-selector syntax is excluded. + +Unknown or shadowed receivers, ambiguous type paths, unsupported expressions, +raw pointers and exhausted receiver-depth inference remain unresolved. These +facts do not establish complete field-use coverage, class cohesion or god-object +defects. Rebuild graphs to obtain the additive access records. AST cache semantics +advance from 7 to 8; evidence/graph schemas, existing producer capabilities and +package version are unchanged. Historical realizations remain immutable. + ## Rust indexed method receivers Rust call extraction follows bounded field and scalar-index receiver syntax diff --git a/MIGRATION.md b/MIGRATION.md index 5dec9cc24..8b01f3726 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,11 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Rebuild Rust graphs to receive newly emitted field-access references. AST cache +semantics version 8 invalidates earlier facts automatically; stored graphs and +historical realizations are not rewritten. The added `member-access` references +preserve source occurrences but do not classify reads/writes or prove cohesion. + MCP neighbor responses now include exact destination records in `structuredContent.result` with schema `compass.query.neighbors/1`. Consumers that need machine identities should read those records instead of parsing text diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index e2202f1d9..03d609cae 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "7"; +pub const AST_CACHE_VERSION: &str = "8"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index 58e9be2ee..1fb2f4c77 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -990,6 +990,11 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< fs::create_dir_all(cache_root.join("compass-out/cache/ast/v2/e1"))?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/v3/e1"))?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/v5/e1"))?; + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v7/e1"))?; + fs::write( + cache_root.join("compass-out/cache/ast/v7/e1/stale.msgpack"), + "Rust facts without field-access occurrences", + )?; fs::write( cache_root.join("compass-out/cache/ast/v5/e1/stale.msgpack"), "stale Java spread parameter and array argument facts", @@ -1032,6 +1037,7 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< assert!(!cache_root.join("compass-out/cache/ast/v2").exists()); assert!(!cache_root.join("compass-out/cache/ast/v3").exists()); assert!(!cache_root.join("compass-out/cache/ast/v5").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v7").exists()); let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index 531b86bbe..bc5ebf0e8 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -5029,8 +5029,8 @@ impl<'source> DirectEvidenceState<'source> { rust_qualify_evidence_path(self, &receiver.owner, &nominal, receiver.source_start) } - // Only called for receiver syntax containing an index. Unsupported forms - // stay unresolved instead of falling back to the collection's method. + // Bounded source types for indexed method receivers and field accesses. + // Unsupported forms stay unresolved instead of falling back to a name. fn rust_indexed_receiver_type( &self, owner: &DeclarationContext, @@ -5772,6 +5772,7 @@ impl<'source> DirectEvidenceState<'source> { match node.kind() { "use_declaration" => return Ok(()), "call_expression" => self.add_rust_call(node, &active)?, + "field_expression" => self.add_rust_field_access(node, &active)?, "macro_invocation" => self.add_rust_macro_invocation(node, &active)?, _ => {} } @@ -5901,6 +5902,73 @@ impl<'source> DirectEvidenceState<'source> { Ok(()) } + fn add_rust_field_access( + &mut self, + node: Node<'_>, + owner: &DeclarationContext, + ) -> Result<(), EvidenceError> { + if self.overlaps_parser_error(node) { + return Ok(()); + } + // In `receiver.method()` the outer selector names a method. Its + // nested receiver fields still receive their own traversal visits. + let selector = node + .parent() + .filter(|parent| parent.kind() == "generic_function") + .unwrap_or(node); + if selector.parent().is_some_and(|parent| { + parent.kind() == "call_expression" + && parent + .child_by_field_name("function") + .is_some_and(|function| function.id() == selector.id()) + }) { + return Ok(()); + } + let (Some(receiver), Some(field)) = ( + node.child_by_field_name("value"), + node.child_by_field_name("field"), + ) else { + return Ok(()); + }; + let spelling = self.text(field); + let qualifier = self.text(receiver); + if spelling.is_empty() || qualifier.is_empty() { + return Ok(()); + } + let qualified_name = self + .rust_indexed_receiver_type(owner, receiver, 32) + .and_then(|source_type| self.rust_indexed_type_name(&source_type)) + .map(|receiver_type| rust_join_qualified(&receiver_type, &spelling)); + let occurrence_id = self.builder.occur_with_context( + SemanticRole::MemberAccess, + &owner.fact_id, + &spelling, + Some(&qualifier), + Some(&owner.scope_id), + Some("member"), + range_for_node(self.source_file, field), + )?; + self.builder.relate( + CandidateRelation::AccessesMember, + &owner.fact_id, + Some(&occurrence_id), + None, + &spelling, + ResolutionConstraint { + exact_language: Some(self.language.to_owned()), + scope_id: Some(owner.scope_id.clone()), + qualified_name, + allowed_target_kinds: vec!["field".to_owned()], + // An unknown receiver stays qualified but unresolved. No + // terminal-name fallback, method target, or external field + // invention can establish a state-sharing relationship. + allow_external: false, + ..ResolutionConstraint::default() + }, + )?; + Ok(()) + } + fn add_rust_call( &mut self, call: Node<'_>, diff --git a/crates/compass-languages/tests/rust_field_access.rs b/crates/compass-languages/tests/rust_field_access.rs new file mode 100644 index 000000000..817161938 --- /dev/null +++ b/crates/compass-languages/tests/rust_field_access.rs @@ -0,0 +1,128 @@ +use std::error::Error; +use std::path::Path; + +use compass_languages::{ + CandidateRelation, Engine, EvidenceLimits, SemanticRole, validate_evidence, +}; + +type FieldTarget = (String, Option); + +fn targets(source: &str) -> Result, Box> { + let evidence = Engine::default() + .extract_source(Path::new("src/lib.rs"), source.as_bytes())? + .semantic_evidence + .ok_or("missing Rust evidence")?; + validate_evidence(&evidence, EvidenceLimits::default())?; + let mut result = Vec::new(); + for candidate in &evidence.candidates { + if candidate.relation != CandidateRelation::AccessesMember { + continue; + } + assert_eq!(candidate.constraints.allowed_target_kinds, ["field"]); + assert!(!candidate.constraints.allow_external); + assert!(candidate.binding_id.is_none()); + let occurrence = evidence + .occurrences + .iter() + .find(|o| Some(&o.id) == candidate.occurrence_id.as_ref()) + .ok_or("missing member occurrence")?; + assert_eq!(occurrence.role, SemanticRole::MemberAccess); + assert!(occurrence.qualifier.is_some()); + let start = usize::try_from(occurrence.range.start_byte)?; + let end = usize::try_from(occurrence.range.end_byte)?; + let spelling = source + .get(start..end) + .ok_or("invalid member source range")?; + assert_eq!(spelling, candidate.target_spelling); + result.push(( + spelling.to_owned(), + candidate.constraints.qualified_name.clone(), + )); + } + result.sort(); + Ok(result) +} + +#[test] +fn self_field_uses_keep_each_occurrence_and_skip_method_selectors() -> Result<(), Box> { + let source = "// λ\nstruct State { value: usize } impl State { fn get(&self) -> usize { self.value } fn bump(&mut self) { self.value = self.value + 1; self.get(); } }"; + assert_eq!( + targets(source)?, + vec![("value".into(), Some("crate::State::value".into())); 3] + ); + Ok(()) +} + +#[test] +fn typed_parameters_nested_fields_and_indexes_keep_nominal_owners() -> Result<(), Box> { + let source = "struct Item { value: usize } struct State { items: Vec } fn run(state: &State, item: &Item) { let _ = item.value; let _ = state.items[0].value; }"; + assert_eq!( + targets(source)?, + vec![ + ("items".into(), Some("crate::State::items".into())), + ("value".into(), Some("crate::Item::value".into())), + ("value".into(), Some("crate::Item::value".into())), + ] + ); + Ok(()) +} + +#[test] +fn unknown_and_shadowed_receivers_do_not_inherit_outer_types() -> Result<(), Box> { + let source = "struct Item { value: usize } fn run(item: &Item) { { let item = unknown(); let _ = item.value; } let _ = item.value; for item in unknown() { let _ = item.value; } let _ = (|item| item.value)(unknown()); }"; + assert_eq!( + targets(source)?, + vec![ + ("value".into(), None), + ("value".into(), None), + ("value".into(), None), + ("value".into(), Some("crate::Item::value".into())), + ] + ); + Ok(()) +} + +#[test] +fn ambiguous_imports_unknown_index_and_raw_pointers_remain_unresolved() -> Result<(), Box> +{ + for source in [ + "use crate::a::Item; use crate::b::Item; fn run(item: &Item) { let _ = item.value; }", + "struct Item { value: usize } fn run(items: Vec, index: Unknown) { let _ = items[index].value; }", + "struct Item { value: usize } fn run(item: *const Item) { let _ = item.value; }", + ] { + assert_eq!(targets(source)?, vec![("value".into(), None)], "{source}"); + } + Ok(()) +} + +#[test] +fn receiver_wrappers_and_trait_impl_self_keep_field_identity() -> Result<(), Box> { + let source = "struct Item { value: usize } trait Read { fn read(&self) -> usize; } impl Read for Item { fn read(&self) -> usize { (&self).value } }"; + assert_eq!( + targets(source)?, + vec![("value".into(), Some("crate::Item::value".into()))] + ); + Ok(()) +} + +#[test] +fn bounded_receiver_syntax_never_falls_back_to_a_field_name() -> Result<(), Box> { + let source = format!( + "struct Item {{ value: usize }} fn run(item: Item) {{ let _ = {}item{}.value; }}", + "(".repeat(40), + ")".repeat(40) + ); + assert_eq!(targets(&source)?, vec![("value".into(), None)]); + Ok(()) +} + +#[test] +fn generic_method_selectors_are_not_fields_but_callable_fields_are_contacts() +-> Result<(), Box> { + let source = "struct Item { callback: fn() } impl Item { fn method(&self) {} fn run(&self) { self.method::(); (self.callback)(); } }"; + assert_eq!( + targets(source)?, + vec![("callback".into(), Some("crate::Item::callback".into()))] + ); + Ok(()) +} diff --git a/crates/compass-resolve/tests/rust_field_access.rs b/crates/compass-resolve/tests/rust_field_access.rs new file mode 100644 index 000000000..329b69348 --- /dev/null +++ b/crates/compass-resolve/tests/rust_field_access.rs @@ -0,0 +1,145 @@ +use std::collections::{BTreeSet, HashMap}; +use std::error::Error; +use std::path::Path; + +use compass_graph::{BuildEvidence, normalize_v1}; +use compass_languages::{Engine, Extraction}; +use compass_model::code_graph::{EdgeKind, NodeKind}; +use compass_resolve::resolve; + +fn extract(file: &str, source: &str) -> Result> { + Ok(Engine::default().extract_source(Path::new(file), source.as_bytes())?) +} + +#[test] +fn cross_file_field_accesses_publish_exact_occurrences_and_preserve_order() +-> Result<(), Box> { + let model = "pub struct Item { pub value: usize }\n"; + let caller = + "use crate::model::Item;\nfn run(item: &mut Item) {\n item.value = item.value + 1;\n}\n"; + let directory = tempfile::tempdir()?; + std::fs::create_dir_all(directory.path().join("src"))?; + std::fs::write(directory.path().join("src/model.rs"), model)?; + std::fs::write(directory.path().join("src/lib.rs"), caller)?; + let sources = HashMap::from([ + ("src/model.rs".into(), model.into()), + ("src/lib.rs".into(), caller.into()), + ]); + let inputs = vec![ + extract("src/model.rs", model)?, + extract("src/lib.rs", caller)?, + ]; + let resolved = resolve(&inputs, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let build = BuildEvidence::from_extraction(directory.path(), &resolved, "sha256:field-access")?; + let graph = normalize_v1(resolved, build)?; + let run = graph + .nodes + .iter() + .find(|n| n.name == "run()") + .ok_or("missing run")?; + let field = graph + .nodes + .iter() + .find(|n| n.name == "value" && n.kind == NodeKind::Field) + .ok_or("missing field")?; + let edges = graph + .links + .iter() + .filter(|e| e.source == run.id && e.target == field.id) + .collect::>(); + assert_eq!(edges.len(), 2, "{:#?}", graph.links); + let mut starts = BTreeSet::new(); + for edge in edges { + assert_eq!(edge.kind, EdgeKind::References); + assert!( + edge.occurrence_rule + .as_ref() + .is_some_and(|rule| rule.as_str().starts_with("universal-member-access-")) + ); + let site = edge + .relationship_site + .as_ref() + .ok_or("missing occurrence")?; + assert_eq!(site.file, "src/lib.rs"); + assert_eq!(site.start_line, 3); + let start = usize::try_from(site.start_byte)?; + let end = usize::try_from(site.end_byte)?; + assert_eq!(caller.get(start..end), Some("value")); + assert!(starts.insert(start)); + assert!(edge.evidence.iter().any(|e| e.anchors.contains(site))); + } + let mut reversed = inputs; + reversed.reverse(); + let second = resolve(&reversed, &sources); + let build = BuildEvidence::from_extraction(directory.path(), &second, "sha256:field-access")?; + let second = normalize_v1(second, build)?; + assert_eq!( + serde_json::to_value(&graph.nodes)?, + serde_json::to_value(&second.nodes)? + ); + assert_eq!( + serde_json::to_value(&graph.links)?, + serde_json::to_value(&second.links)? + ); + Ok(()) +} + +#[test] +fn shadowed_unknown_and_duplicate_field_targets_never_publish_a_convenient_match() +-> Result<(), Box> { + for source in [ + "struct Item { value: usize } fn run(item: &Item) { let item = unknown(); let _ = item.value; }", + "struct Item { value: usize } fn run(item: Unknown) { let _ = item.value; }", + "struct Item { value: usize, value: usize } fn run(item: &Item) { let _ = item.value; }", + "struct Item { value: usize } fn run(item: &Item) { let _ = (|item| item.value)(unknown()); }", + "mod other { pub struct Item { pub value: usize } } use other::*; fn run(item: Unknown) { let _ = item.value; }", + ] { + let resolved = resolve( + &[extract("src/lib.rs", source)?], + &HashMap::from([("src/lib.rs".into(), source.into())]), + ); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + assert!( + !resolved + .edges + .iter() + .any(|e| e.string("relation") == "accesses"), + "{source}: {:#?}", + resolved.edges + ); + } + Ok(()) +} + +#[test] +fn nested_receivers_publish_field_contacts_but_never_method_contacts() -> Result<(), Box> +{ + let source = "struct Item { value: usize } impl Item { fn get(&self) -> usize { self.value } } struct State { item: Item } impl State { fn run(&self) { self.item.get(); } }"; + let resolved = resolve( + &[extract("src/lib.rs", source)?], + &HashMap::from([("src/lib.rs".into(), source.into())]), + ); + let fields = resolved + .nodes + .iter() + .filter(|n| n.string("symbol_kind") == "field") + .map(|n| n.id.as_str()) + .collect::>(); + let accesses = resolved + .edges + .iter() + .filter(|e| e.string("relation") == "accesses") + .collect::>(); + assert_eq!(accesses.len(), 2, "{:#?}", resolved.edges); + for edge in accesses { + assert!(fields.contains(edge.target.as_str())); + } + assert!( + resolved + .edges + .iter() + .any(|e| e.string("relation") == "calls") + ); + Ok(()) +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index dcc20e353..7bfb0a69b 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2762,7 +2762,7 @@ Code inspection locates concrete producer gaps at baseline `7aef6a0c`: relabeling arbitrary state as callable. The existing member-access candidate projects to a `references` edge with -`member-access` context, so qualified field-access evidence can use an existing +member-access provenance, so qualified field-access evidence can use an existing relationship representation. The next production work belongs in language fact emission and qualified resolution, with shadowing/ambiguity negatives, precise occurrence anchors, bounded lookup, cache invalidation and affected diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index 7c7f79826..546e913b8 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -864,3 +864,15 @@ source-proven inherited members. Parser recovery diagnostics use bounded, nonempty source ranges even when Tree-sitter reports a zero-width missing token. Do not infer support for another language or framework from file extensions, raw graph output, or total node and edge counts. + + +### Rust field contacts + +Rust field expressions now emit `MemberAccess` occurrences and `AccessesMember` +candidates under the existing `Members` capability. Candidates preserve the +field identifier range, source owner, receiver spelling and bounded qualified +receiver type when available. Targets are restricted to field declarations; +unknown receivers remain unresolved, and method call selectors do not become +field contacts. The existing graph projection emits `references` with +member-access provenance. This is state-contact evidence, not read/write or alias +analysis. Disposable AST semantics version 8 invalidates pre-access facts. From b4b8300d30c872946ae0c37c8ba4d915d9049925 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:12:40 -0700 Subject: [PATCH 80/97] Record verified Rust state-access recovery and paired MCP evidence --- benchmarks/agent_query/README.md | 15 + .../rust_state_access_development_review.json | 476 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 89 ++++ 3 files changed, 580 insertions(+) create mode 100644 benchmarks/agent_query/rust_state_access_development_review.json diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index c1968ab0e..a019835a1 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -508,3 +508,18 @@ hashes, all candidate/connecting records and the auditor's own code hash. It preserves Graphify's undirected container flag; stored endpoint order is not a native directed-path claim. Same-agent review and purposive development scope remain explicit. + + +### Rust field-access correction + +`rust_state_access_development_registration.json` freezes the correction and a +known-ID public neighbor control. `rust_state_access_development_review.json` +records the five-repository rebuild: Compass now supports 4/20 registered access +sites (all four Rust sites), versus Graphify's unchanged 0/20. The four other +Compass graphs are byte-identical. WalkDir adds 174 field-reference records and +changes 122 community assignments while preserving all earlier nodes/edges. +The four public neighbor requests retrieve the selected identities and anchors +for Compass; Graphify lacks those fields. This known-subject gain does not +establish overall superiority, source precision for every added edge, improved +clustering, read/write effects or god-object defects. Raw captures and replay +scripts remain under the registered external artifact directory. diff --git a/benchmarks/agent_query/rust_state_access_development_review.json b/benchmarks/agent_query/rust_state_access_development_review.json new file mode 100644 index 000000000..42f595d10 --- /dev/null +++ b/benchmarks/agent_query/rust_state_access_development_review.json @@ -0,0 +1,476 @@ +{ + "schema": "compass.rust-state-access-development-review/1", + "scope": "Known development sources and fixed source-access sites. Full-graph coverage plus a separate known-ID-assisted public retrieval control; not held-out, representative god-object classification, exhaustive edge precision, read/write analysis, clustering quality or overall superiority.", + "productCommit": "390406c1", + "registrationCommits": [ + "4a1be265", + "56d7d559" + ], + "registrationSha256": "badb11879b8a646ee208a27f521dd012417f16b8ddc561d2ee24a7d388b1fc70", + "sourceRegistrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "baselineReviewSha256": "7716b9c160a6b113d0ee14af146e279e7de191f393ef92862737673f45675afc", + "artifactDirectory": "rust-state-access-01", + "sourceSha256": { + "crates/compass-languages/src/evidence/build.rs": "e27fa5637afe57e2b21c28029878746806798121b212cbc8d8ec47f4fad7d48f", + "crates/compass-files/src/cache.rs": "5e7850bb2ce97c17e91cd763c4a36a6c7f2260edb082278c16ffcbdfbd6e9c73", + "crates/compass-files/tests/contracts.rs": "a9f37e4bbf888a91034fb9332fac3f7efe1872bc9c7752aad4372ffc04c616e6", + "crates/compass-languages/tests/rust_field_access.rs": "0c8bdcc6e1a5a5e881518ec1cd0e60a04afda16044c98a98d037da9979a0202e", + "crates/compass-resolve/tests/rust_field_access.rs": "afc0aeac1a8e74418667a0507aa9c84cbadf8787820c0f4df1f25e9a1d351730" + }, + "compassBinarySha256": "a1692336191eff63849846eb91c1804798381d9e9f1a4ff1180a73a37c61f505", + "graphResults": { + "compass": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 6, + "contactSupported": 4, + "selectedLineSupported": 4, + "status": { + "contact_edge": 4, + "missing_state": 8, + "no_contact_edge": 8 + } + }, + "graphify": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 0, + "contactSupported": 0, + "selectedLineSupported": 0, + "status": { + "missing_state": 20 + } + } + }, + "graphDeltas": [ + { + "repository": "chi", + "oldGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "newGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "nodesBefore": 729, + "nodesAfter": 729, + "edgesBefore": 1914, + "edgesAfter": 1914, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "click", + "oldGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "newGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "nodesBefore": 4264, + "nodesAfter": 4264, + "edgesBefore": 6387, + "edgesAfter": 6387, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "jsoup", + "oldGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "newGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "nodesBefore": 6116, + "nodesAfter": 6116, + "edgesBefore": 21110, + "edgesAfter": 21110, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "redux", + "oldGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "newGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "nodesBefore": 3503, + "nodesAfter": 3503, + "edgesBefore": 5653, + "edgesAfter": 5653, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "walkdir", + "oldGraphSha256": "e68fbe798dcf7422184736971dcdfc29d577e1e37269ea9c43456b9f6af54cb3", + "newGraphSha256": "f7818d1c9aaf93b0b80d0f1a3a3fd2179b60ae17f9278185c9c3eb529f531034", + "nodesBefore": 288, + "nodesAfter": 288, + "edgesBefore": 1206, + "edgesAfter": 1380, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 122, + "addedEdges": 174, + "removedEdges": 0, + "changedEdges": 0 + } + ], + "publicSummary": { + "compass": { + "tasks": 4, + "fieldNeighborIdentity": 4, + "selectedLineAnchored": 4, + "textBytes": 10308, + "wireBytes": 139303 + }, + "graphify": { + "tasks": 4, + "fieldNeighborIdentity": 0, + "selectedLineAnchored": 0, + "textBytes": 1519, + "wireBytes": 1904 + } + }, + "publicRows": [ + { + "tool": "compass", + "method": "handle_entry", + "state": "deferred_dirs", + "fieldNeighborIdentity": true, + "selectedLineAnchored": true, + "textBytes": 3839, + "wireBytes": 55221 + }, + { + "tool": "compass", + "method": "get_deferred_dir", + "state": "deferred_dirs", + "fieldNeighborIdentity": true, + "selectedLineAnchored": true, + "textBytes": 1601, + "wireBytes": 18620 + }, + { + "tool": "compass", + "method": "push", + "state": "oldest_opened", + "fieldNeighborIdentity": true, + "selectedLineAnchored": true, + "textBytes": 3264, + "wireBytes": 46122 + }, + { + "tool": "compass", + "method": "pop", + "state": "oldest_opened", + "fieldNeighborIdentity": true, + "selectedLineAnchored": true, + "textBytes": 1604, + "wireBytes": 19340 + }, + { + "tool": "graphify", + "method": "handle_entry", + "state": "deferred_dirs", + "fieldNeighborIdentity": false, + "selectedLineAnchored": false, + "textBytes": 421, + "wireBytes": 518 + }, + { + "tool": "graphify", + "method": "get_deferred_dir", + "state": "deferred_dirs", + "fieldNeighborIdentity": false, + "selectedLineAnchored": false, + "textBytes": 367, + "wireBytes": 463 + }, + { + "tool": "graphify", + "method": "push", + "state": "oldest_opened", + "fieldNeighborIdentity": false, + "selectedLineAnchored": false, + "textBytes": 473, + "wireBytes": 571 + }, + { + "tool": "graphify", + "method": "pop", + "state": "oldest_opened", + "fieldNeighborIdentity": false, + "selectedLineAnchored": false, + "textBytes": 258, + "wireBytes": 352 + } + ], + "addedOccurrenceConsistency": 174, + "validation": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 4.31, + "testCounts": null, + "metadataSha256": "f86b0a171fb71e1337471cdc2bf28079df4868ceee2802671bf88729d3037734", + "logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "language-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-languages", + "--test", + "rust_field_access", + "--test", + "rust_index_receivers", + "--test", + "rust_universal_conformance", + "--test", + "rust_universal_phase2", + "--locked" + ], + "exitCode": 0, + "seconds": 8.32, + "testCounts": [ + 45, + 0, + 0 + ], + "metadataSha256": "1f0cedad05d94a9d10f3cef56ee0941582f247c2a8e39b05efa0e26b604fc489", + "logSha256": "4041f3ff2f138574fa4a3ddeb44283eb4716642799695fd5d29a93ac516565b3" + }, + { + "name": "resolver-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-resolve", + "--test", + "rust_field_access", + "--test", + "universal_evidence", + "--test", + "universal_resolution", + "--locked" + ], + "exitCode": 0, + "seconds": 1.29, + "testCounts": [ + 237, + 0, + 0 + ], + "metadataSha256": "6277dd30dbf6d092c8db7132ce31a07bcccca41cb16be717ee916eb92ba23122", + "logSha256": "14aa5273ffdd547b5cd3f9c58ce4aad1de0126ee2d973b9752a90621ca314573" + }, + { + "name": "cache-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-files", + "--test", + "contracts", + "--locked" + ], + "exitCode": 0, + "seconds": 1.13, + "testCounts": [ + 33, + 0, + 0 + ], + "metadataSha256": "a60275701716533ee5f1ec66e6d30c6a6b928cc5c4011403ea8d2235abce13ed", + "logSha256": "07f20992c401957f5dfc5d98f6ba45169569edbd67a5c6bf00c1b603213632a6" + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 1.21, + "testCounts": null, + "metadataSha256": "49269c798c0ea6a34cce01cd9697d9a017f4479b5d29cecb78e81980302c67f1", + "logSha256": "00e61713e19859fe5a2aa621594e80ce8447dfdaca7fa6adcdd310bae37c4406" + }, + { + "name": "focused-clippy", + "argv": [ + "cargo", + "clippy", + "-p", + "compass-languages", + "-p", + "compass-resolve", + "--test", + "rust_field_access", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 0.68, + "testCounts": null, + "metadataSha256": "5e4afd17a18c55c067028eaeba4f9609b5ba53bcfd21e1f2bc5f9a5845a20e70", + "logSha256": "b0b99394e88f6c488aef84cb39e9bccb7e1fa624de1ffebf9ff580d9f54e0415" + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 136.93, + "testCounts": [ + 1106, + 0, + 2 + ], + "metadataSha256": "673338d8608edc04cde894666158884e88254c0b93b3da5f1dc40b8f82398614", + "logSha256": "93ef2074f2aba191d8114e02b12c7c5fad24ab8dc0c1d6c1759544828675e4b0" + }, + { + "name": "product-tests", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 67.24, + "testCounts": [ + 9, + 0, + 0 + ], + "metadataSha256": "030ac33a8880c4804ac13015ee412974d7ae680c865b2ce46063993b2aa1aff7", + "logSha256": "f401555994c89c6569f6a9aa327caf5e8998f48c9b7f3adc80d559634acd94c8" + }, + { + "name": "boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.26, + "testCounts": null, + "metadataSha256": "fcfd459348474f184179f16612ef3509909aec6adc4a18d659cd759ff7eb707f", + "logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "fixtures", + "argv": [ + "bash", + "scripts/qualify_code_graph_v1.sh", + "--fixtures-only" + ], + "exitCode": 0, + "seconds": 910.11, + "testCounts": [ + 6, + 0, + 0 + ], + "metadataSha256": "8a70d1d7e50db27558a4fc9b668340604631f587d089fa531f9f462ec22d3267", + "logSha256": "03e528b0d4e5af1253b4a41e2b1b50bf4b672f64d8c56c26208a868c17a5d0a7" + }, + { + "name": "benchmarks", + "argv": [ + "python3", + "-m", + "unittest", + "discover", + "-s", + "benchmarks/agent_query/tests" + ], + "exitCode": 0, + "seconds": 2.38, + "testCounts": null, + "metadataSha256": "7850844ae639f7122cbbf6afa4778989c70e7312631dfdb4b55bb6c80453b2e2", + "logSha256": "6e6e1f38ab8af6fcf53fef9d922c261be18d5c5b5bd470bf964c227087c2c825" + } + ], + "artifacts": { + "run.json": "529f842b347802c59687560b6338bda8038fe704579c3e5434ee6654bcb59aad", + "state-access-registration.json": "a4c74826dd45d4d52379e70e9f8b11dc29495a6d7de37590ee263ec4b816aabb", + "review.json": "35960cdaf41f1ab99b965a319ff78997872f030a9fb56e8cf982d3b9af558567", + "delta-summary.json": "cbea1f337c19ce7cd3c736e1274ce34fe2e972e9637b9d1ee78a5c20cf4e47fb", + "public-capture.json": "2fa6f9f365bb0a566144ebe47b68bc4e4c716791ebcac3686ce483f7809091b0", + "verification.json": "63eeb921b301712184a5391e85aa33bbcdac96ae4b6719ae63764e97397b4721", + "added-occurrence-consistency.json": "b285a3564cd14dc08532f6a4b02968c0e065b984e73108c1f591e58bb5b5e011", + "evaluate.py": "b3d8e0dd1af6681456751ae569a1d40dee942ce460343a8eeb909b3ae475c9b9", + "public.py": "bb6af84e80791722a0321c121939dba087591188a6374bc5c0413151234a6e18", + "verify.py": "99ad35c67b09ec4cee2335ce48e3e2fb6f4d3229504b2da8a61ca8797b109339", + "run.py": "e7e01fedde8cebd9b9994d59e58bbdaacc343d93a3076ffadb3349cacc12368b", + "validate.py": "dbb79f30742668c11f482a374117fb9bb7b852c3c37c23e9a4d917ef5ecd283e", + "summarize.py": "a4a97d5fd22277e7a65c2733f64ded7c57597187d7de45931f0440aea89d0e49" + }, + "limitations": [ + "The two state slots create four related Rust observations, not four independent repositories. Other sixteen source-access sites remain unsupported.", + "All old WalkDir nodes/edges are retained; 122 community assignments change. No clustering-quality judgment follows.", + "174 added records pass occurrence/endpoint consistency; comprehensive semantic precision is unproven.", + "jsoup and Redux retain partial-publication warnings for two omitted edges each; their graphs are byte-identical controls.", + "MCP requests use native defaults and exact IDs supplied as task inputs. Compass carries richer and much larger payloads. No efficiency/latency win.", + "Same-agent source selection/adjudication; no independent human or compiler semantic oracle.", + "Six fail-before extraction tests; early integration fixture errors and a mistaken context-field assertion were corrected. Member-access provenance is present; optional context is not required.", + "Qualification logs retain existing partial-fixture publication, linker and unused-mut warnings. Full hosted CI/platform/packaging results are not claimed." + ], + "ciCommandVerification": { + "argv": [ + "cargo", + "test", + "-p", + "compass-languages", + "-p", + "compass-resolve", + "--test", + "rust_field_access", + "--locked" + ], + "exitCode": 0, + "metadataSha256": "e2bb261068b0af36acd657b9d78442036b3cdf0ff86dee18a8bd308cf1168fbb", + "logSha256": "fff186f2a65c1904a55d18a7953178cd73e9c2317422bddf4b6c3b2542c0e281" + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 7bfb0a69b..4d840dec8 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2787,6 +2787,95 @@ real-repository review replays byte-for-byte. No Rust/JavaScript tests or extraction gates were rerun for this benchmark/documentation-only checkpoint; previous production validation remains tied to its earlier commit. +## Rust field-access correction and paired navigation control + +Registrations `4a1be265` and `56d7d559` fix the production contract, unchanged +20-site comparison and four known-ID-assisted public neighbor requests before +rebuilt graphs or follow-up outputs were inspected. Production commit +`390406c1` emits Rust `MemberAccess` occurrences and qualified `AccessesMember` +candidates for explicit field expressions. It reuses bounded source-type +inference and existing universal resolution, restricts targets to fields, +retains unknown/shadowed receivers as unresolved, and excludes method selectors. +Parallel occurrences keep exact field-identifier anchors. It does not infer +read/write effects, aliasing, independent responsibilities or god-object defects. + +AST cache semantics advance from 7 to 8 so older facts rebuild. Evidence/graph +schemas, advertised producer capabilities and package version stay unchanged; +published historical realizations are immutable. Graphs must be rebuilt to +receive the new evidence. The CI workflow now runs both new integration suites +explicitly because its library/binary test invocations would otherwise skip them. + +All five Compass graphs were rebuilt from the same pinned, read-only sources. +Graphify's frozen native graphs remain unchanged. The original 0/20 versus +0/20 report is preserved; it is not rewritten with the improved graph. + +| Evidence | Compass before | Compass after | Graphify | +| --- | ---: | ---: | ---: | +| Registered state-contact access sites, all five repositories | 0/20 | **4/20** | 0/20 | +| Registered Rust access sites | 0/4 | **4/4** | 0/4 | +| State slots represented, all five repositories | 6/10 | 6/10 | 0/10 | + +The four recovered sites link `IntoIter::handle_entry` and `get_deferred_dir` +to `deferred_dirs`, and `push` and `pop` to `oldest_opened`, at the registered +lines. Java and TypeScript still miss their eight selected contact edges; +Go and Python still lack the four selected state-slot declarations. Both tools +continue to identify all twenty accessing callable coordinates. + +Chi, Click, jsoup and Redux graphs are byte-for-byte identical to their frozen +controls, including all communities. jsoup and Redux still report two omitted +edges each; this checkpoint does not repair those partial publications. WalkDir +keeps all 288 nodes and all 1,206 earlier edge records unchanged, adds 174 +member-access references, and changes 122 node community assignments. It now +has 1,380 edge records. Recomputed clustering is an observable consequence, not +proof of improved communities. The verifier checks every added record's field +target kind, exact identifier bytes, enclosing source extent and provenance; +this is occurrence consistency rather than a compiler/type-resolution oracle +or full semantic precision review of all 174 records. + +The separate public MCP arm provides each tool its own exact callable IDs as +explicit task inputs, then makes one unfiltered `get_neighbors` request per +method. All eight requests succeed. Compass exposes the field identity and the +selected source-line occurrence in **4/4** replies; Graphify exposes neither +in **0/4**. Compass's complete neighbor nodes and records match its new graph. +This is a known-ID retrieval control with native defaults, not natural-language +identity discovery or authored responsibility explanation. It is not held-out. + +Compass returns 10,308 text / 139,303 full response bytes; Graphify returns +1,519 / 1,904. The larger Compass responses retain full records and repeated +occurrences; no token-efficiency or latency advantage is claimed. The scoped +four-site gain must not be substituted for the earlier five-language source +explanation comparison, where Graphify retained a 15/20 versus 14/20 lead. + +All six initial extraction regressions failed before the change. The final +seven extraction tests and three resolver/publication tests cover direct and +nested fields, indexed receivers, lexical shadowing, unknown/raw-pointer +receivers, duplicate fields, ambiguous imports, depth exhaustion, trait impls, +callable fields versus method selectors, cross-file targets, parallel anchors +and input-order determinism. An integration fixture initially lacked physical +source inventory and checked the wrong raw kind key; both were corrected. +Another assertion exposed an audit wording error: `member-access` is retained +in provenance, not necessarily in the optional `context` field. The earlier +wording is corrected. Focused Clippy also caught and corrected a test-helper +type-complexity warning. Failed attempts remain in the external logs. + +Final validation passed formatting, 45 Rust language integration tests, +237 resolver integration tests, 33 cache contracts, workspace and focused-test +Clippy, 1,106 workspace tests (2 ignored), 9 product tests, the product boundary, +full code-graph fixture qualification (including Markdown and the independent +React release-binary fixture gate), and 148 benchmark tests. The new CI command +also passes all ten new integration tests. The evaluated debug binary matches +the qualifying debug binary, and validated source hashes match `390406c1`. +Warnings retained in the logs include fixture publication omissions, a linker +warning and an existing unused-mut test warning; passing does not mean the logs +are warning-free. Hosted platform/packaging/browser matrices are not claimed. + +Verification and artifact hashes are recorded in +`benchmarks/agent_query/rust_state_access_development_review.json`; the complete +capture, fresh graph manifests, graph deltas, raw MCP transcripts, source checks, +verifier and validation logs are under `rust-state-access-01`. Remaining Java, +TypeScript, Go and Python state evidence, actual cohesion/god-object judgments, +authored explanations and fresh held-out confirmation remain unfinished. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From d1b701b51599f8445fee1b2981ed9d0e912bf84e Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:22:36 -0700 Subject: [PATCH 81/97] audit: register Java field scope compiler challenge --- .../java_state_access/ScopeFixture.java | 167 +++++++ .../java_state_scope_registration.json | 443 ++++++++++++++++++ 2 files changed, 610 insertions(+) create mode 100644 benchmarks/agent_query/fixtures/java_state_access/ScopeFixture.java create mode 100644 benchmarks/agent_query/java_state_scope_registration.json diff --git a/benchmarks/agent_query/fixtures/java_state_access/ScopeFixture.java b/benchmarks/agent_query/fixtures/java_state_access/ScopeFixture.java new file mode 100644 index 000000000..40c94fb24 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_state_access/ScopeFixture.java @@ -0,0 +1,167 @@ +package audit; + +import java.util.function.IntSupplier; +import java.util.function.IntUnaryOperator; + +// Compiler-checkable development fixture; no product runtime dependency on Java. +public class ScopeFixture extends Base { + int value; // field scope_value + Cell slot; // field scope_slot + Cell[] items; // field scope_items + RuntimeException failure; // field scope_failure + Token resource; // field scope_resource + Object pattern; // field scope_pattern + static int staticValue; // field scope_static + + ScopeFixture(int value) { + this.value = value; // case constructor: scope_value + } + int explicit() { + return this.value; // case explicit_this: scope_value + } + int implicit() { + return value; // case implicit_this: scope_value + } + int repeated() { + return value + this.value; // case repeated: scope_value,scope_value + } + int parameter(int value) { + return value; // case parameter_shadow: - + } + void block() { + sink(value); // case block_before: scope_value + { + int value = 7; + sink(value); // case block_shadow: - + } + sink(value); // case block_after: scope_value + } + int beforeLocal() { + sink(value); // case before_local: scope_value + int value = 3; + return value; // case after_local: - + } + void loop() { + for (int value = 0; value < 1; value++) { + sink(value); // case for_shadow: - + } + sink(value); // case for_after: scope_value + } + int enhanced() { + for (Cell slot : items) { // case enhanced_iterable: scope_items + sink(slot.value); // case enhanced_binding: cell_value + } + return slot.value; // case enhanced_after: scope_slot,cell_value + } + void lambdas() { + IntUnaryOperator a = value -> value + 1; // case lambda_shadow: - + IntSupplier b = () -> this.value; // case lambda_this: scope_value + IntSupplier c = () -> value; // case lambda_implicit: scope_value + sink(a.applyAsInt(b.getAsInt()) + c.getAsInt()); + } + void catches() { + try { throw new RuntimeException(); } + catch (RuntimeException failure) { + sink(failure == null ? 0 : 1); // case catch_shadow: - + } + sink(failure == null ? 0 : 1); // case catch_after: scope_failure + } + void resources() { + try (Token resource = new Token()) { + sink(resource.value); // case resource_binding: token_value + } catch (RuntimeException e) { + sink(resource.value); // case resource_catch: scope_resource,token_value + } + sink(resource.value); // case resource_after: scope_resource,token_value + } + int pattern(Object input) { + if (input instanceof Cell slot) { + return slot.value; // case pattern_true: cell_value + } + return slot.value; // case pattern_after: scope_slot,cell_value + } + int negatedPattern() { + if (!(pattern instanceof Cell slot)) return 0; // case pattern_input: scope_pattern + return slot.value; // case pattern_flow: cell_value + } + int typed(Cell cell) { + return cell.value; // case typed_parameter: cell_value + } + int cast(Object cell) { + return ((Cell) cell).value; // case cast_receiver: cell_value + } + int chain() { + return slot.value; // case field_chain: scope_slot,cell_value + } + int indexed() { + return items[0].value; // case array_receiver: scope_items,cell_value + } + int parent() { + return super.value; // case super_field: base_value + } + int staticBinding(Cell cell, Shadow shadow) { + return cell.value; // case declared_receiver: cell_value + } + int subtype(Shadow cell) { + return cell.value; // case hiding_receiver: shadow_value + } + int generic(T cell) { + return cell.value; // case generic_bound: cell_value + } + static int statics() { + return staticValue + ScopeFixture.staticValue; // case static_fields: scope_static,scope_static + } + int value() { return 1; } + int methodSelector() { + return value(); // case method_not_field: - + } + void declarators() { + int first = value, value = 2; // case declarator_order: scope_value + sink(first + value); // case declarator_shadow: - + } + class Inner { + int value; // field inner_value + int own() { + return value; // case inner_own: inner_value + } + int outer() { + return ScopeFixture.this.value; // case qualified_this: scope_value + } + } + class Inherited extends Base { + int inherited() { + return value; // case inherited_beats_outer: base_value + } + } + IntSupplier anonymous() { + return new IntSupplier() { + int value; // field anonymous_value + public int getAsInt() { + return this.value + ScopeFixture.this.value; // case anonymous_this: anonymous_value,scope_value + } + }; + } + int local() { + class Local { + int value; // field local_value + int get() { + return this.value; // case local_class_this: local_value + } + } + return new Local().get(); + } + static void sink(int value) {} +} +class Base { + int value; // field base_value +} +class Cell { + int value; // field cell_value +} +class Shadow extends Cell { + int value; // field shadow_value +} +class Token implements AutoCloseable { + int value; // field token_value + public void close() {} +} diff --git a/benchmarks/agent_query/java_state_scope_registration.json b/benchmarks/agent_query/java_state_scope_registration.json new file mode 100644 index 000000000..c679f72a3 --- /dev/null +++ b/benchmarks/agent_query/java_state_scope_registration.json @@ -0,0 +1,443 @@ +{ + "schema": "compass.java-state-scope-registration/1", + "baselineCommit": "b4b8300d30c872946ae0c37c8ba4d915d9049925", + "scope": "Source-authored synthetic development challenge for Java field scope and receiver selection. Not a real-repository result, held-out evaluation, query benchmark or god-object classifier.", + "source": "benchmarks/agent_query/fixtures/java_state_access/ScopeFixture.java", + "sourceSha256": "bfc306296dbc95809b0d0171ed770785ce442ba6253dd98ecbbf37cff9730335", + "compilerPolicy": "Compile without processors using an already installed JDK with --release 17 and line tables; do not execute fixture code. Verify each registered line against every compiled method, including lambda and nested class methods. Resolve bytecode field owner through source class declarations; retain all field instructions and unexpected targets. Exclude compiler-generated fields only when absent from the declared fixture field inventory. Duplicated compiler instructions at the same source line remain visible.", + "scoringPolicy": "Compare occurrence-bearing forward state contact records against compiled field contacts per registered line. Missing field nodes and missing occurrence anchors remain failures. Negative lines are explicit scope controls, reported separately; zero emitted accesses cannot establish positive precision. No read/write classification score. Preserve multiplicity and all mismatches. Synthetic results never replace the unchanged 20-site five-repository registration.", + "nextProductContract": "Use an AST lexical scope index for Java field accesses, with explicit shadow barriers. Preserve field-only qualified resolution, exact occurrence anchors, ambiguity and unsupported targets. Cover ordinary this/unqualified and typed receivers while avoiding method/type/declaration names. Inheritance, patterns, local/anonymous classes and bounded generic lookup must be source-proven or remain unsupported, never fall back to an outer or same-named field.", + "fields": { + "scope_value": { + "owner": "audit.ScopeFixture", + "name": "value", + "line": 8, + "text": " int value; // field scope_value" + }, + "scope_slot": { + "owner": "audit.ScopeFixture", + "name": "slot", + "line": 9, + "text": " Cell slot; // field scope_slot" + }, + "scope_items": { + "owner": "audit.ScopeFixture", + "name": "items", + "line": 10, + "text": " Cell[] items; // field scope_items" + }, + "scope_failure": { + "owner": "audit.ScopeFixture", + "name": "failure", + "line": 11, + "text": " RuntimeException failure; // field scope_failure" + }, + "scope_resource": { + "owner": "audit.ScopeFixture", + "name": "resource", + "line": 12, + "text": " Token resource; // field scope_resource" + }, + "scope_pattern": { + "owner": "audit.ScopeFixture", + "name": "pattern", + "line": 13, + "text": " Object pattern; // field scope_pattern" + }, + "scope_static": { + "owner": "audit.ScopeFixture", + "name": "staticValue", + "line": 14, + "text": " static int staticValue; // field scope_static" + }, + "inner_value": { + "owner": "audit.ScopeFixture$Inner", + "name": "value", + "line": 123, + "text": " int value; // field inner_value" + }, + "anonymous_value": { + "owner": "audit.ScopeFixture$1", + "name": "value", + "line": 138, + "text": " int value; // field anonymous_value" + }, + "local_value": { + "owner": "audit.ScopeFixture$1Local", + "name": "value", + "line": 146, + "text": " int value; // field local_value" + }, + "base_value": { + "owner": "audit.Base", + "name": "value", + "line": 156, + "text": " int value; // field base_value" + }, + "cell_value": { + "owner": "audit.Cell", + "name": "value", + "line": 159, + "text": " int value; // field cell_value" + }, + "shadow_value": { + "owner": "audit.Shadow", + "name": "value", + "line": 162, + "text": " int value; // field shadow_value" + }, + "token_value": { + "owner": "audit.Token", + "name": "value", + "line": 165, + "text": " int value; // field token_value" + } + }, + "cases": [ + { + "id": "constructor", + "line": 17, + "text": " this.value = value; // case constructor: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "explicit_this", + "line": 20, + "text": " return this.value; // case explicit_this: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "implicit_this", + "line": 23, + "text": " return value; // case implicit_this: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "repeated", + "line": 26, + "text": " return value + this.value; // case repeated: scope_value,scope_value", + "expectedFields": [ + "scope_value", + "scope_value" + ] + }, + { + "id": "parameter_shadow", + "line": 29, + "text": " return value; // case parameter_shadow: -", + "expectedFields": [] + }, + { + "id": "block_before", + "line": 32, + "text": " sink(value); // case block_before: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "block_shadow", + "line": 35, + "text": " sink(value); // case block_shadow: -", + "expectedFields": [] + }, + { + "id": "block_after", + "line": 37, + "text": " sink(value); // case block_after: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "before_local", + "line": 40, + "text": " sink(value); // case before_local: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "after_local", + "line": 42, + "text": " return value; // case after_local: -", + "expectedFields": [] + }, + { + "id": "for_shadow", + "line": 46, + "text": " sink(value); // case for_shadow: -", + "expectedFields": [] + }, + { + "id": "for_after", + "line": 48, + "text": " sink(value); // case for_after: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "enhanced_iterable", + "line": 51, + "text": " for (Cell slot : items) { // case enhanced_iterable: scope_items", + "expectedFields": [ + "scope_items" + ] + }, + { + "id": "enhanced_binding", + "line": 52, + "text": " sink(slot.value); // case enhanced_binding: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "enhanced_after", + "line": 54, + "text": " return slot.value; // case enhanced_after: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ] + }, + { + "id": "lambda_shadow", + "line": 57, + "text": " IntUnaryOperator a = value -> value + 1; // case lambda_shadow: -", + "expectedFields": [] + }, + { + "id": "lambda_this", + "line": 58, + "text": " IntSupplier b = () -> this.value; // case lambda_this: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "lambda_implicit", + "line": 59, + "text": " IntSupplier c = () -> value; // case lambda_implicit: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "catch_shadow", + "line": 65, + "text": " sink(failure == null ? 0 : 1); // case catch_shadow: -", + "expectedFields": [] + }, + { + "id": "catch_after", + "line": 67, + "text": " sink(failure == null ? 0 : 1); // case catch_after: scope_failure", + "expectedFields": [ + "scope_failure" + ] + }, + { + "id": "resource_binding", + "line": 71, + "text": " sink(resource.value); // case resource_binding: token_value", + "expectedFields": [ + "token_value" + ] + }, + { + "id": "resource_catch", + "line": 73, + "text": " sink(resource.value); // case resource_catch: scope_resource,token_value", + "expectedFields": [ + "scope_resource", + "token_value" + ] + }, + { + "id": "resource_after", + "line": 75, + "text": " sink(resource.value); // case resource_after: scope_resource,token_value", + "expectedFields": [ + "scope_resource", + "token_value" + ] + }, + { + "id": "pattern_true", + "line": 79, + "text": " return slot.value; // case pattern_true: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "pattern_after", + "line": 81, + "text": " return slot.value; // case pattern_after: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ] + }, + { + "id": "pattern_input", + "line": 84, + "text": " if (!(pattern instanceof Cell slot)) return 0; // case pattern_input: scope_pattern", + "expectedFields": [ + "scope_pattern" + ] + }, + { + "id": "pattern_flow", + "line": 85, + "text": " return slot.value; // case pattern_flow: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "typed_parameter", + "line": 88, + "text": " return cell.value; // case typed_parameter: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "cast_receiver", + "line": 91, + "text": " return ((Cell) cell).value; // case cast_receiver: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "field_chain", + "line": 94, + "text": " return slot.value; // case field_chain: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ] + }, + { + "id": "array_receiver", + "line": 97, + "text": " return items[0].value; // case array_receiver: scope_items,cell_value", + "expectedFields": [ + "scope_items", + "cell_value" + ] + }, + { + "id": "super_field", + "line": 100, + "text": " return super.value; // case super_field: base_value", + "expectedFields": [ + "base_value" + ] + }, + { + "id": "declared_receiver", + "line": 103, + "text": " return cell.value; // case declared_receiver: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "hiding_receiver", + "line": 106, + "text": " return cell.value; // case hiding_receiver: shadow_value", + "expectedFields": [ + "shadow_value" + ] + }, + { + "id": "generic_bound", + "line": 109, + "text": " return cell.value; // case generic_bound: cell_value", + "expectedFields": [ + "cell_value" + ] + }, + { + "id": "static_fields", + "line": 112, + "text": " return staticValue + ScopeFixture.staticValue; // case static_fields: scope_static,scope_static", + "expectedFields": [ + "scope_static", + "scope_static" + ] + }, + { + "id": "method_not_field", + "line": 116, + "text": " return value(); // case method_not_field: -", + "expectedFields": [] + }, + { + "id": "declarator_order", + "line": 119, + "text": " int first = value, value = 2; // case declarator_order: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "declarator_shadow", + "line": 120, + "text": " sink(first + value); // case declarator_shadow: -", + "expectedFields": [] + }, + { + "id": "inner_own", + "line": 125, + "text": " return value; // case inner_own: inner_value", + "expectedFields": [ + "inner_value" + ] + }, + { + "id": "qualified_this", + "line": 128, + "text": " return ScopeFixture.this.value; // case qualified_this: scope_value", + "expectedFields": [ + "scope_value" + ] + }, + { + "id": "inherited_beats_outer", + "line": 133, + "text": " return value; // case inherited_beats_outer: base_value", + "expectedFields": [ + "base_value" + ] + }, + { + "id": "anonymous_this", + "line": 140, + "text": " return this.value + ScopeFixture.this.value; // case anonymous_this: anonymous_value,scope_value", + "expectedFields": [ + "anonymous_value", + "scope_value" + ] + }, + { + "id": "local_class_this", + "line": 148, + "text": " return this.value; // case local_class_this: local_value", + "expectedFields": [ + "local_value" + ] + } + ] +} From 716446141d8c1e9e0d945487b041d03f5e72e423 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:32:52 -0700 Subject: [PATCH 82/97] audit: verify Java field scope against compiler and paired graphs --- benchmarks/agent_query/README.md | 47 + .../agent_query/java_state_scope_audit.py | 411 +++++ .../java_state_scope_baseline.json | 1608 ++++++++++++++++ .../agent_query/java_state_scope_oracle.json | 1637 +++++++++++++++++ .../tests/test_java_state_scope.py | 197 ++ ...ode-graph-intelligence-audit-2026-09-26.md | 78 + 6 files changed, 3978 insertions(+) create mode 100644 benchmarks/agent_query/java_state_scope_audit.py create mode 100644 benchmarks/agent_query/java_state_scope_baseline.json create mode 100644 benchmarks/agent_query/java_state_scope_oracle.json create mode 100644 benchmarks/agent_query/tests/test_java_state_scope.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index a019835a1..b4e2e7cfb 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -523,3 +523,50 @@ for Compass; Graphify lacks those fields. This known-subject gain does not establish overall superiority, source precision for every added edge, improved clustering, read/write effects or god-object defects. Raw captures and replay scripts remain under the registered external artifact directory. + + +### Java field-scope compiler challenge + +`java_state_scope_registration.json` registers 44 synthetic development cases +before either graph capture. The source fixture exercises positive field +selection, repeated occurrences, local/parameter shadowing, block and loop +lifetimes, lambda/catch/resource bindings, flow-scoped patterns, inheritance, +field hiding, static access, nested/anonymous/local classes, casts and array receivers. +The installed Corretto 17.0.8 compiler confirms all 44 source expectations: +36 positive cases, eight negative controls and 45 field occurrences. + +`java_state_scope_oracle.json` retains each compiler instruction and source line. +`java_state_scope_baseline.json` records fresh native graph captures of that same +fixture: Compass publishes 12/14 registered fields, Graphify 0/14, and neither +publishes a contact to any registered field. Both therefore miss all 45 selected +occurrences. Empty negatives do not establish positive precision. Caller +ownership is explicitly unscored by this target/line inventory. These synthetic +results do not replace the five-repository state-access comparison or establish +cohesion, god-object defects or overall superiority. + +Capture with an already installed JDK; classes and logs belong on the external +workspace volume. This compiles with processors disabled and never runs fixture +code. There is no JDK requirement for normal Compass execution or Python unit +tests. The parser is a bounded fixture-specific oracle, not a general Java +compiler front end. In particular, nonconstant fixture fields avoid constant +folding; source/class-field inventory mismatches fail, and only compiler-marked +synthetic fields are excluded from source targets. + +```sh +python3 -m benchmarks.agent_query.java_state_scope_audit \ + --registration benchmarks/agent_query/java_state_scope_registration.json \ + --java-home /path/to/installed/jdk \ + --artifacts /Volumes/Workspace/CrabData/java-scope-new/capture \ + --output /Volumes/Workspace/CrabData/java-scope-new/oracle.json +python3 -m unittest benchmarks.agent_query.tests.test_java_state_scope +``` + +To replay the committed oracle, omit `--java-home`, point `--artifacts` at the +saved `java-state-scope-01/capture` directory, and use +`--output benchmarks/agent_query/java_state_scope_oracle.json --verify`. +Add `--graph-manifest /path/to/java-state-scope-01/graphs.json` and +`--oracle benchmarks/agent_query/java_state_scope_oracle.json` to replay the +baseline into +`--output benchmarks/agent_query/java_state_scope_baseline.json --verify`. Compilation/disassembly outputs, compiler identities, source, +registration, class files and graph hashes are retained. Historical exploratory +reports remain in the external artifact directory. diff --git a/benchmarks/agent_query/java_state_scope_audit.py b/benchmarks/agent_query/java_state_scope_audit.py new file mode 100644 index 000000000..78a8b73ee --- /dev/null +++ b/benchmarks/agent_query/java_state_scope_audit.py @@ -0,0 +1,411 @@ +"""Compiler-backed Java field-scope development oracle, separate from product CI. + +Requires a supplied installed JDK only when capturing. Never executes Java fixture +code. Verification replays bounded, hashed class files and javap transcripts. +This is not a general Java bytecode analyzer or a real-repository scorecard. +""" +import argparse +from collections import Counter +import hashlib +import json +from pathlib import Path +import re +import struct + +from benchmarks.agent_query.runner import run_bounded +from benchmarks.agent_query.state_access_audit import MAX_GRAPH_BYTES, candidates, occurrence, read + +LIMIT = 4 * 1024 * 1024 +MAX_CLASSES = 64 + + +def support_hashes(): + return {name: sha(Path(__file__).with_name(name)) + for name in ('runner.py', 'state_access_audit.py')} + + +def sha(path): + return hashlib.sha256(read(path, LIMIT)).hexdigest() + + +def registration(path): + data = json.loads(read(path, LIMIT)) + if data['schema'] != 'compass.java-state-scope-registration/1': + raise ValueError('unknown registration schema') + source = Path(data['source']) + if sha(source) != data['sourceSha256']: + raise ValueError('source hash mismatch') + lines = read(source, LIMIT).decode().splitlines() + ids, sites = set(), set() + for case in data['cases']: + if case['id'] in ids or case['line'] in sites: + raise ValueError('duplicate case identity or source line') + ids.add(case['id']) + sites.add(case['line']) + if lines[case['line'] - 1] != case['text']: + raise ValueError('case source witness mismatch') + if any(field not in data['fields'] for field in case['expectedFields']): + raise ValueError('unknown expected field') + for field in data['fields'].values(): + if lines[field['line'] - 1] != field['text']: + raise ValueError('field source witness mismatch') + return data + + +class ClassReader: + """Read only constant pool, ancestry and field metadata; no code execution.""" + def __init__(self, data): + if len(data) > LIMIT: + raise ValueError('oversized class') + self.data, self.position = data, 0 + + def take(self, size): + end = self.position + size + if end > len(self.data): + raise ValueError('truncated class') + data, self.position = self.data[self.position:end], end + return data + + def u2(self): + return struct.unpack('>H', self.take(2))[0] + + def parse(self): + if self.take(4) != b'\xca\xfe\xba\xbe': + raise ValueError('invalid class magic') + minor, major = self.u2(), self.u2() + pool = [None] * self.u2() + index = 1 + while index < len(pool): + tag = self.take(1)[0] + if tag == 1: + # Fixture identifiers are ASCII; reject unsupported modified UTF-8. + pool[index] = self.take(self.u2()).decode('utf-8') + elif tag in (7, 8, 16, 19, 20): + pool[index] = (tag, self.u2()) + elif tag in (3, 4, 9, 10, 11, 12, 17, 18): + self.take(4) + elif tag in (5, 6): + self.take(8) + index += 1 + elif tag == 15: + self.take(3) + else: + raise ValueError(f'unsupported constant tag {tag}') + index += 1 + + def string(index): + if not 0 < index < len(pool) or not isinstance(pool[index], str): + raise ValueError('invalid string constant') + return pool[index] + + def class_name(index): + if index == 0: + return None + if not 0 < index < len(pool) or not isinstance(pool[index], tuple) or pool[index][0] != 7: + raise ValueError('invalid class constant') + return string(pool[index][1]).replace('/', '.') + + self.u2() # Class flags are irrelevant to field identity. + owner, parent = class_name(self.u2()), class_name(self.u2()) + interfaces = [class_name(self.u2()) for _ in range(self.u2())] + fields = [] + for _ in range(self.u2()): + flags, name, descriptor = self.u2(), string(self.u2()), string(self.u2()) + synthetic = bool(flags & 0x1000) + for _ in range(self.u2()): + attribute = string(self.u2()) + size = struct.unpack('>I', self.take(4))[0] + self.take(size) + synthetic |= attribute == 'Synthetic' + fields.append(dict(name=name, descriptor=descriptor, synthetic=synthetic)) + return dict(owner=owner, parent=parent, interfaces=interfaces, fields=fields, + major=major, minor=minor) + + +def parse_javap(text, owner): + """Associate each field instruction with the preceding bytecode line entry.""" + methods, current = [], None + for line in text.splitlines(): + # -p -c -l -s emits two-space declaration headers and deeper code. + if re.match(r'^ \S', line): + if '(' in line and line.endswith(';'): + current = dict(method=line.strip(), instructions=[], lines=[], offsets=[]) + methods.append(current) + elif line == ' static {};': + current = dict(method='', instructions=[], lines=[], offsets=[]) + methods.append(current) + else: + current = None + if current is None: + continue + instruction = re.match(r'^\s+(\d+):\s+(\w+)\b', line) + if instruction: + offset, opcode = int(instruction[1]), instruction[2] + current['offsets'].append(offset) + if opcode in {'getfield', 'putfield', 'getstatic', 'putstatic'}: + field = re.search(r'// Field ([^: ]+):([^ ]+)\s*$', line) + if not field: + raise ValueError('unrecognized field instruction') + reference = field[1].replace('/', '.') + target_owner, _, name = reference.rpartition('.') + current['instructions'].append(dict(offset=offset, opcode=opcode, + owner=target_owner or owner, name=name, descriptor=field[2], raw=line)) + entry = re.fullmatch(r'\s+line (\d+): (\d+)', line) + if entry: + current['lines'].append((int(entry[2]), int(entry[1]))) + if not methods: + raise ValueError('no methods in javap output') + occurrences, covered = [], set() + for method in methods: + offsets = method['offsets'] + if offsets != sorted(set(offsets)): + raise ValueError('duplicate or unordered instruction offsets') + line_map = sorted(method['lines']) + if len({offset for offset, _ in line_map}) != len(line_map): + raise ValueError('ambiguous line mapping') + if any(offset not in offsets for offset, _ in line_map): + raise ValueError('line mapping outside instructions') + covered.update(line for _, line in line_map) + for instruction in method['instructions']: + preceding = [entry for entry in line_map if entry[0] <= instruction['offset']] + if not preceding: + raise ValueError('field instruction missing source line') + occurrences.append(dict(instruction, line=preceding[-1][1], + method=method['method'], bytecodeClass=owner)) + return occurrences, covered + + +def field_declaration(classes, owner, name, descriptor): + """JVM field resolution: declared field, interfaces, then superclass. + + This fixture has no interface fields. Reject multiple interface matches + instead of treating inheritance order as source-level disambiguation. + """ + def find(current, visiting): + if current in visiting or len(visiting) >= MAX_CLASSES: + raise ValueError('cyclic or excessive field hierarchy') + if current not in classes: + return [] + cls = classes[current] + direct = [dict(field, owner=current) for field in cls['fields'] + if field['name'] == name and field['descriptor'] == descriptor] + if direct: + return direct + visiting = visiting | {current} + interfaces = [field for interface in cls['interfaces'] for field in find(interface, visiting)] + return interfaces or find(cls['parent'], visiting) + result = find(owner, set()) + if len(result) != 1: + raise ValueError(f'field declaration missing or ambiguous: {owner}.{name}:{descriptor}') + return result[0] + + +def capture(reg_path, directory, java_home): + reg = registration(reg_path) + directory.mkdir(parents=True, exist_ok=False) + commands = [] + + def run(name, argv): + result = run_bounded(tuple(map(str, argv)), cwd=Path.cwd(), timeout_seconds=60, + stdout_path=directory / f'{name}.stdout', stderr_path=directory / f'{name}.stderr') + commands.append(dict(argv=list(result.argv), exitCode=result.exit_code, + timedOut=result.timed_out, outputLimited=result.output_limited, + stdout=f'{name}.stdout', stderr=f'{name}.stderr')) + if result.exit_code or result.timed_out or result.output_limited: + raise ValueError(f'command failed; preserved logs: {name}') + + javac, javap = java_home / 'bin/javac', java_home / 'bin/javap' + run('compiler-version', [javac, '-version']) + run('javac', [javac, '--release', '17', '-proc:none', '-implicit:none', + '-g:lines,vars,source', '-d', directory / 'classes', Path(reg['source']).resolve()]) + files = sorted((directory / 'classes').rglob('*.class')) + if not 0 < len(files) <= MAX_CLASSES: + raise ValueError('invalid class count') + artifacts = [] + for index, path in enumerate(files): + cls = ClassReader(read(path, LIMIT)).parse() + name = f'class-{index:02}' + run(name, [javap, '-p', '-c', '-l', '-s', '-classpath', directory / 'classes', cls['owner']]) + artifacts.append(dict(owner=cls['owner'], binary=str(path.relative_to(directory)), + disassembly=f'{name}.stdout')) + hashes = {str(path.relative_to(directory)): sha(path) + for path in sorted(directory.rglob('*')) if path.is_file()} + manifest = dict(schema='compass.java-state-scope-capture/1', + registrationSha256=sha(reg_path), sourceSha256=reg['sourceSha256'], + jdkRelease=read(java_home / 'release', LIMIT).decode(), + compilerSha256=sha(javac), disassemblerSha256=sha(javap), + commands=commands, classes=artifacts, hashes=hashes) + (directory / 'capture.json').write_text(json.dumps(manifest, indent=2) + '\n') + registration(reg_path) # Reject concurrent fixture changes. + + +def evaluate(reg_path, directory): + reg = registration(reg_path) + manifest = json.loads(read(directory / 'capture.json', LIMIT)) + if (manifest['schema'] != 'compass.java-state-scope-capture/1' + or manifest['registrationSha256'] != sha(reg_path) + or manifest['sourceSha256'] != reg['sourceSha256']): + raise ValueError('capture registration mismatch') + for name, expected in manifest['hashes'].items(): + path = (directory / name).resolve() + path.relative_to(directory.resolve()) + if sha(path) != expected: + raise ValueError('capture file hash mismatch') + if not manifest['commands'] or any(c['exitCode'] or c['timedOut'] or c['outputLimited'] + for c in manifest['commands']): + raise ValueError('unsuccessful capture') + classes, occurrences, covered = {}, [], set() + if not 0 < len(manifest['classes']) <= MAX_CLASSES: + raise ValueError('invalid class count') + for artifact in manifest['classes']: + if any(artifact[k] not in manifest['hashes'] for k in ('binary', 'disassembly')): + raise ValueError('unhashed class input') + cls = ClassReader(read(directory / artifact['binary'], LIMIT)).parse() + if cls['owner'] != artifact['owner'] or cls['owner'] in classes: + raise ValueError('class identity mismatch') + classes[cls['owner']] = cls + found, lines = parse_javap(read(directory / artifact['disassembly'], LIMIT).decode(), cls['owner']) + occurrences.extend(found) + covered.update(lines) + inventory = {(v['owner'], v['name']): k for k, v in reg['fields'].items()} + compiled = {(owner, field['name']) for owner, cls in classes.items() + for field in cls['fields'] if not field['synthetic']} + if compiled != set(inventory) or len(inventory) != len(reg['fields']): + raise ValueError('compiled field inventory differs from registration') + for occurrence in occurrences: + declaration = field_declaration(classes, occurrence['owner'], occurrence['name'], occurrence['descriptor']) + occurrence['declaration'] = declaration + occurrence['field'] = inventory.get((declaration['owner'], declaration['name'])) + if occurrence['field'] is None and not declaration['synthetic']: + raise ValueError('unexpected nonsynthetic field') + rows = [] + for case in reg['cases']: + if case['line'] not in covered: + raise ValueError(f'case has no compiled source line: {case["id"]}') + records = [o for o in occurrences if o['line'] == case['line']] + actual = [o['field'] for o in records if o['field'] is not None] + rows.append(dict(case, observedFields=actual, compilerInstructions=records, + agrees=Counter(actual) == Counter(case['expectedFields']))) + return dict(schema='compass.java-state-scope-oracle/1', registrationSha256=sha(reg_path), + scriptSha256=sha(Path(__file__)), supportSha256=support_hashes(), captureSha256=sha(directory / 'capture.json'), + compiler=manifest['jdkRelease'], classes=classes, + summary=dict(cases=len(rows), agreed=sum(row['agrees'] for row in rows), + positiveCases=sum(bool(row['expectedFields']) for row in rows), + negativeCases=sum(not row['expectedFields'] for row in rows), + fieldOccurrences=sum(len(row['expectedFields']) for row in rows)), cases=rows) + + + +def graph_inventory(graph, tool, reg): + """Inventory field endpoints and occurrence targets, without caller scoring. + + This is a diagnostic prerequisite, not a positive field-edge precision score: + caller ownership still requires its own source-coordinate/native regression. + """ + nodes = {node['id']: node for node in graph['nodes']} + if len(nodes) != len(graph['nodes']): + raise ValueError('duplicate graph node ID') + if any(edge['source'] not in nodes or edge['target'] not in nodes for edge in graph['links']): + raise ValueError('dangling graph endpoint') + file = Path(reg['source']).name + fields = {key: candidates(graph['nodes'], tool, file, field['line'], field['name']) + for key, field in reg['fields'].items()} + if tool == 'compass': + fields = {key: [node for node in choices if node.get('kind') == 'field'] + for key, choices in fields.items()} + unique = {choices[0]['id']: key for key, choices in fields.items() if len(choices) == 1} + all_ids = {node['id'] for choices in fields.values() for node in choices} + contacts = sorted((edge for edge in graph['links'] if edge['target'] in all_ids + and edge.get('kind' if tool == 'compass' else 'relation') + in {'references', 'reads', 'writes'}), key=lambda e: json.dumps(e, sort_keys=True)) + rows = [] + for case in reg['cases']: + records = [edge for edge in contacts if occurrence(edge, tool, file, case['line'])] + observed = [unique[edge['target']] for edge in records if edge['target'] in unique] + expected = Counter(case['expectedFields']) + actual = Counter(observed) + rows.append(dict(id=case['id'], line=case['line'], expectedFields=case['expectedFields'], + observedUniqueTargets=observed, connectingRecords=records, + targetOccurrencesMatched=sum((actual & expected).values()), + unexpectedTargets=list((actual - expected).elements()))) + return dict(graphDirected=graph.get('directed'), nodes=len(nodes), edges=len(graph['links']), + fieldCandidates=fields, allContactRecords=contacts, cases=rows, + summary=dict(fieldDeclarations=len(fields), uniqueFields=len(unique), + missingFields=sum(not choices for choices in fields.values()), + ambiguousFields=sum(len(choices) > 1 for choices in fields.values()), + fieldContactRecords=len(contacts), registeredFieldOccurrences=sum(len(c['expectedFields']) for c in reg['cases']), + occurrenceTargetsMatched=sum(r['targetOccurrencesMatched'] for r in rows), + negativeCases=sum(not c['expectedFields'] for c in reg['cases']), + negativeCasesWithContact=sum(not c['expectedFields'] and bool(c['connectingRecords']) for c in rows), + callerOwnershipScored=False, edgePrecision=None)) + + +def compare_graphs(reg_path, oracle_path, manifest_path): + reg = registration(reg_path) + oracle = json.loads(read(oracle_path, LIMIT)) + if (oracle['registrationSha256'] != sha(reg_path) + or oracle['summary']['cases'] != len(reg['cases']) + or oracle['summary']['agreed'] != len(reg['cases'])): + raise ValueError('compiler oracle does not confirm the registration') + manifest = json.loads(read(manifest_path, LIMIT)) + if manifest['registrationSha256'] != sha(reg_path) or manifest['sourceSha256'] != reg['sourceSha256']: + raise ValueError('graph registration mismatch') + reports = {} + for tool in ('compass', 'graphify'): + metadata = manifest['tools'][tool] + raw = read(Path(metadata['graph']), MAX_GRAPH_BYTES) + if hashlib.sha256(raw).hexdigest() != metadata['graphSha256'] or metadata['exitCode'] != 0: + raise ValueError('graph capture mismatch') + reports[tool] = graph_inventory(json.loads(raw), tool, reg) + return dict(schema='compass.java-state-scope-baseline/1', registrationSha256=sha(reg_path), + scriptSha256=sha(Path(__file__)), supportSha256=support_hashes(), oracleSha256=sha(oracle_path), + graphManifestSha256=sha(manifest_path), scope=reg['scope'], + limitations=['Target/line diagnostic only; caller ownership and positive edge precision are unscored.', + 'The compiler confirms this nonconstant fixture, not a general source-access oracle.', + 'Graphify stored endpoint order is preserved; an undirected graph is not directed path proof.', + 'Synthetic controls do not replace the five real-repository comparisons or prove god-object quality.'], + inputs=manifest, tools=reports) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--registration', type=Path, required=True) + parser.add_argument('--artifacts', type=Path, required=True) + parser.add_argument('--graph-manifest', type=Path) + parser.add_argument('--oracle', type=Path, help='verified compiler review for graph comparison') + parser.add_argument('--java-home', type=Path, help='capture into a new directory using this installed JDK') + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--verify', action='store_true') + args = parser.parse_args() + if bool(args.graph_manifest) != bool(args.oracle): + parser.error('--graph-manifest and --oracle must be supplied together') + if args.graph_manifest and args.java_home: + parser.error('graph comparison never recaptures the compiler') + if args.java_home: + if args.verify: + parser.error('verification never recaptures') + capture(args.registration, args.artifacts, args.java_home) + if args.graph_manifest: + # Recompute the compiler evidence, not just its saved summary. + actual = (json.dumps(evaluate(args.registration, args.artifacts), indent=2) + '\n').encode() + if read(args.oracle, LIMIT) != actual: + raise ValueError('compiler oracle differs on replay') + report = compare_graphs(args.registration, args.oracle, args.graph_manifest) + else: + report = evaluate(args.registration, args.artifacts) + payload = (json.dumps(report, indent=2) + '\n').encode() + if args.verify: + if read(args.output, LIMIT) != payload: + raise ValueError('oracle review differs on replay') + else: + with args.output.open('xb') as stream: + stream.write(payload) + summary = ({tool: result['summary'] for tool, result in report['tools'].items()} + if args.graph_manifest else report['summary']) + print(json.dumps(summary, indent=2)) + if not args.graph_manifest and report['summary']['agreed'] != report['summary']['cases']: + raise SystemExit('source/compiler disagreement; do not score this oracle') + + +if __name__ == '__main__': + main() diff --git a/benchmarks/agent_query/java_state_scope_baseline.json b/benchmarks/agent_query/java_state_scope_baseline.json new file mode 100644 index 000000000..8bd73ab31 --- /dev/null +++ b/benchmarks/agent_query/java_state_scope_baseline.json @@ -0,0 +1,1608 @@ +{ + "schema": "compass.java-state-scope-baseline/1", + "registrationSha256": "3a12b108736b5341ed09f0f9c66aa874e2612ef8d8e26afc542692fe0bdf445a", + "scriptSha256": "9a7f160a8c2a4a4ae9ed1c33d978aee953f2b9aafe9b8467d5b141a220272123", + "supportSha256": { + "runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "state_access_audit.py": "2bf95b707f18f34c9df4461a26f011b83d86f166efacfaab652f8e6231161058" + }, + "oracleSha256": "38fb8d38bccbc395cd06e5ec42fa7edcd7735f9ef4839e9a727d4a2af851a1e7", + "graphManifestSha256": "2073c22e8c0a2fcfe456f0ec837c3d1451b305501950e4f63d81ee052e795e66", + "scope": "Source-authored synthetic development challenge for Java field scope and receiver selection. Not a real-repository result, held-out evaluation, query benchmark or god-object classifier.", + "limitations": [ + "Target/line diagnostic only; caller ownership and positive edge precision are unscored.", + "The compiler confirms this nonconstant fixture, not a general source-access oracle.", + "Graphify stored endpoint order is preserved; an undirected graph is not directed path proof.", + "Synthetic controls do not replace the five real-repository comparisons or prove god-object quality." + ], + "inputs": { + "registrationSha256": "3a12b108736b5341ed09f0f9c66aa874e2612ef8d8e26afc542692fe0bdf445a", + "sourceSha256": "bfc306296dbc95809b0d0171ed770785ce442ba6253dd98ecbbf37cff9730335", + "compassBinary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/rust-state-access-01/compass", + "compassBinarySha256": "a1692336191eff63849846eb91c1804798381d9e9f1a4ff1180a73a37c61f505", + "graphifyBinary": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/graphify-mcp-env/bin/graphify", + "graphifyBinarySha256": "ed90e12baa7ef1820eb6ab994850cecae3b8dd0c9e35ee43cd8763bc5603dcb6", + "graphifyEnvironmentSha256": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535", + "tools": { + "compass": { + "argv": [ + "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/rust-state-access-01/compass", + "extract", + "/Users/haipingfu/.codex/worktrees/58baf490-704c-4d67-9c65-a00a1ca7e005/compass/benchmarks/agent_query/fixtures/java_state_access", + "--code-only", + "--no-viz", + "--store", + "sqlite", + "--out", + "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/java-state-scope-01/graphs/compass" + ], + "exitCode": 0, + "graph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/java-state-scope-01/graphs/compass/compass-out/snapshots/snapshot-1790511939362120000-67292-0/graph.json", + "graphSha256": "667eb129744eee412c13201f261541cd2bb8a2ab5bb9413e095feb8bee683b82", + "stdoutSha256": "c47196698414352df28b4065f3ed15a9f493690b4371bb48cd052e98fb5ab852", + "stderrSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "graphify": { + "argv": [ + "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/graphify-mcp-env/bin/graphify", + "extract", + "/Users/haipingfu/.codex/worktrees/58baf490-704c-4d67-9c65-a00a1ca7e005/compass/benchmarks/agent_query/fixtures/java_state_access", + "--code-only", + "--out", + "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/java-state-scope-01/graphs/graphify" + ], + "exitCode": 0, + "graph": "/Volumes/Workspace/CrabData/compass-evaluations/code-graph-audit-20260926/java-state-scope-01/graphs/graphify/graphify-out/graph.json", + "graphSha256": "161b065248da2f4407e6827761dde90841bba37e984be7c975795a65b2c8aa2d", + "stdoutSha256": "20b38f2f49563f331e37f38e13fecf67966d986ffdaabe291d8775f463b375d3", + "stderrSha256": "5819273003575662cf88b98550563254d739ebd612abe4ee788c1bc4e15a1ceb" + } + } + }, + "tools": { + "compass": { + "graphDirected": true, + "nodes": 53, + "edges": 78, + "fieldCandidates": { + "scope_value": [ + { + "id": "sha256:c120391c9804991eb001a0931b57304659226e7d01ffc53085f83fe821e14682", + "kind": "field", + "name": "value", + "qualifiedName": "audit.ScopeFixture::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 231, + "endByte": 236, + "startLine": 8, + "startColumn": 8, + "endLine": 8, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 231, + "endByte": 236, + "startLine": 8, + "startColumn": 8, + "endLine": 8, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 0, + "label": "ScopeFixture" + } + } + ], + "scope_slot": [ + { + "id": "sha256:c619e464c6b19595928732033a66d53458db7fd9700cf706335d5d847f02946f", + "kind": "field", + "name": "slot", + "qualifiedName": "audit.ScopeFixture::slot", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 268, + "endByte": 272, + "startLine": 9, + "startColumn": 9, + "endLine": 9, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 268, + "endByte": 272, + "startLine": 9, + "startColumn": 9, + "endLine": 9, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 1, + "label": "Cell" + } + } + ], + "scope_items": [ + { + "id": "sha256:2bdbd3f28c364d1b40e7ea4ba2b34dfef3ad736d42f4aa63e8ef4e0d9e5c54c7", + "kind": "field", + "name": "items", + "qualifiedName": "audit.ScopeFixture::items", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 305, + "endByte": 310, + "startLine": 10, + "startColumn": 11, + "endLine": 10, + "endColumn": 16 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 305, + "endByte": 310, + "startLine": 10, + "startColumn": 11, + "endLine": 10, + "endColumn": 16 + } + ] + } + ], + "community": { + "id": 1, + "label": "Cell" + } + } + ], + "scope_failure": [ + { + "id": "sha256:a9c547361cad58532c1cd08f804ac8b9f3d2bb8a6f5af12119d5bef15cda7224", + "kind": "field", + "name": "failure", + "qualifiedName": "audit.ScopeFixture::failure", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 354, + "endByte": 361, + "startLine": 11, + "startColumn": 21, + "endLine": 11, + "endColumn": 28 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 354, + "endByte": 361, + "startLine": 11, + "startColumn": 21, + "endLine": 11, + "endColumn": 28 + } + ] + } + ], + "community": { + "id": 0, + "label": "ScopeFixture" + } + } + ], + "scope_resource": [ + { + "id": "sha256:73ad1f600b37b11263bfa5f1cfad9147cf7df45d0fe8090aa008bb0e744bfe76", + "kind": "field", + "name": "resource", + "qualifiedName": "audit.ScopeFixture::resource", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 396, + "endByte": 404, + "startLine": 12, + "startColumn": 10, + "endLine": 12, + "endColumn": 18 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 396, + "endByte": 404, + "startLine": 12, + "startColumn": 10, + "endLine": 12, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 4, + "label": "Token" + } + } + ], + "scope_pattern": [ + { + "id": "sha256:86a48a4109ea79da64074189aecc6511576c8e008da8236323a2f6bef2b857c5", + "kind": "field", + "name": "pattern", + "qualifiedName": "audit.ScopeFixture::pattern", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 441, + "endByte": 448, + "startLine": 13, + "startColumn": 11, + "endLine": 13, + "endColumn": 18 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 441, + "endByte": 448, + "startLine": 13, + "startColumn": 11, + "endLine": 13, + "endColumn": 18 + } + ] + } + ], + "community": { + "id": 0, + "label": "ScopeFixture" + } + } + ], + "scope_static": [ + { + "id": "sha256:9640b33e89ef020d7198a0109a8603256f072cd8cae801cf6e06794f2a5febe6", + "kind": "field", + "name": "staticValue", + "qualifiedName": "audit.ScopeFixture::staticValue", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 488, + "endByte": 499, + "startLine": 14, + "startColumn": 15, + "endLine": 14, + "endColumn": 26 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 488, + "endByte": 499, + "startLine": 14, + "startColumn": 15, + "endLine": 14, + "endColumn": 26 + } + ] + } + ], + "community": { + "id": 0, + "label": "ScopeFixture" + } + } + ], + "inner_value": [ + { + "id": "sha256:6b7ddf31e47588cb7e38e5d2b7af2fbea587db26b6451eb25a278747be7c5d42", + "kind": "field", + "name": "value", + "qualifiedName": "audit.ScopeFixture::Inner::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 4410, + "endByte": 4415, + "startLine": 123, + "startColumn": 12, + "endLine": 123, + "endColumn": 17 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 4410, + "endByte": 4415, + "startLine": 123, + "startColumn": 12, + "endLine": 123, + "endColumn": 17 + } + ] + } + ], + "community": { + "id": 3, + "label": "Inner" + } + } + ], + "anonymous_value": [], + "local_value": [], + "base_value": [ + { + "id": "sha256:c4d7ec7b8bfad6392d33d030ed93419e9d930010f75a6d724f028bde9945b66a", + "kind": "field", + "name": "value", + "qualifiedName": "audit.Base::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 5384, + "endByte": 5389, + "startLine": 156, + "startColumn": 8, + "endLine": 156, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 5384, + "endByte": 5389, + "startLine": 156, + "startColumn": 8, + "endLine": 156, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 0, + "label": "ScopeFixture" + } + } + ], + "cell_value": [ + { + "id": "sha256:00c165a1eecef001df211ee8af6590b433183f710703bf96f08a6f4749b651b7", + "kind": "field", + "name": "value", + "qualifiedName": "audit.Cell::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 5434, + "endByte": 5439, + "startLine": 159, + "startColumn": 8, + "endLine": 159, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 5434, + "endByte": 5439, + "startLine": 159, + "startColumn": 8, + "endLine": 159, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 1, + "label": "Cell" + } + } + ], + "shadow_value": [ + { + "id": "sha256:4f1db07ec624d79cb8530c7b864ae5e92a35b32a2a59ac2eed1efb570d8fc295", + "kind": "field", + "name": "value", + "qualifiedName": "audit.Shadow::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 5499, + "endByte": 5504, + "startLine": 162, + "startColumn": 8, + "endLine": 162, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 5499, + "endByte": 5504, + "startLine": 162, + "startColumn": 8, + "endLine": 162, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 1, + "label": "Cell" + } + } + ], + "token_value": [ + { + "id": "sha256:f806daf2ddfcccec2f458b73414b365d04a23954587c8ada5efa398fdfcd6d0d", + "kind": "field", + "name": "value", + "qualifiedName": "audit.Token::value", + "language": "java", + "source": { + "file": "ScopeFixture.java", + "startByte": 5577, + "endByte": 5582, + "startLine": 165, + "startColumn": 8, + "endLine": 165, + "endColumn": 13 + }, + "details": { + "type": "symbol", + "data": {} + }, + "evidence": [ + { + "origin": "ast", + "extractor": "compass.languages.java.universal", + "confidence": "exact", + "anchors": [ + { + "file": "ScopeFixture.java", + "startByte": 5577, + "endByte": 5582, + "startLine": 165, + "startColumn": 8, + "endLine": 165, + "endColumn": 13 + } + ] + } + ], + "community": { + "id": 4, + "label": "Token" + } + } + ] + }, + "allContactRecords": [], + "cases": [ + { + "id": "constructor", + "line": 17, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "explicit_this", + "line": 20, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "implicit_this", + "line": 23, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "repeated", + "line": 26, + "expectedFields": [ + "scope_value", + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "parameter_shadow", + "line": 29, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_before", + "line": 32, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_shadow", + "line": 35, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_after", + "line": 37, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "before_local", + "line": 40, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "after_local", + "line": 42, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "for_shadow", + "line": 46, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "for_after", + "line": 48, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_iterable", + "line": 51, + "expectedFields": [ + "scope_items" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_binding", + "line": 52, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_after", + "line": 54, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_shadow", + "line": 57, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_this", + "line": 58, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_implicit", + "line": 59, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "catch_shadow", + "line": 65, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "catch_after", + "line": 67, + "expectedFields": [ + "scope_failure" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_binding", + "line": 71, + "expectedFields": [ + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_catch", + "line": 73, + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_after", + "line": 75, + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_true", + "line": 79, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_after", + "line": 81, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_input", + "line": 84, + "expectedFields": [ + "scope_pattern" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_flow", + "line": 85, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "typed_parameter", + "line": 88, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "cast_receiver", + "line": 91, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "field_chain", + "line": 94, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "array_receiver", + "line": 97, + "expectedFields": [ + "scope_items", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "super_field", + "line": 100, + "expectedFields": [ + "base_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declared_receiver", + "line": 103, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "hiding_receiver", + "line": 106, + "expectedFields": [ + "shadow_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "generic_bound", + "line": 109, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "static_fields", + "line": 112, + "expectedFields": [ + "scope_static", + "scope_static" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "method_not_field", + "line": 116, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declarator_order", + "line": 119, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declarator_shadow", + "line": 120, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "inner_own", + "line": 125, + "expectedFields": [ + "inner_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "qualified_this", + "line": 128, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "inherited_beats_outer", + "line": 133, + "expectedFields": [ + "base_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "anonymous_this", + "line": 140, + "expectedFields": [ + "anonymous_value", + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "local_class_this", + "line": 148, + "expectedFields": [ + "local_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + } + ], + "summary": { + "fieldDeclarations": 14, + "uniqueFields": 12, + "missingFields": 2, + "ambiguousFields": 0, + "fieldContactRecords": 0, + "registeredFieldOccurrences": 45, + "occurrenceTargetsMatched": 0, + "negativeCases": 8, + "negativeCasesWithContact": 0, + "callerOwnershipScored": false, + "edgePrecision": null + } + }, + "graphify": { + "graphDirected": false, + "nodes": 43, + "edges": 62, + "fieldCandidates": { + "scope_value": [], + "scope_slot": [], + "scope_items": [], + "scope_failure": [], + "scope_resource": [], + "scope_pattern": [], + "scope_static": [], + "inner_value": [], + "anonymous_value": [], + "local_value": [], + "base_value": [], + "cell_value": [], + "shadow_value": [], + "token_value": [] + }, + "allContactRecords": [], + "cases": [ + { + "id": "constructor", + "line": 17, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "explicit_this", + "line": 20, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "implicit_this", + "line": 23, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "repeated", + "line": 26, + "expectedFields": [ + "scope_value", + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "parameter_shadow", + "line": 29, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_before", + "line": 32, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_shadow", + "line": 35, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "block_after", + "line": 37, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "before_local", + "line": 40, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "after_local", + "line": 42, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "for_shadow", + "line": 46, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "for_after", + "line": 48, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_iterable", + "line": 51, + "expectedFields": [ + "scope_items" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_binding", + "line": 52, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "enhanced_after", + "line": 54, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_shadow", + "line": 57, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_this", + "line": 58, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "lambda_implicit", + "line": 59, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "catch_shadow", + "line": 65, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "catch_after", + "line": 67, + "expectedFields": [ + "scope_failure" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_binding", + "line": 71, + "expectedFields": [ + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_catch", + "line": 73, + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "resource_after", + "line": 75, + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_true", + "line": 79, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_after", + "line": 81, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_input", + "line": 84, + "expectedFields": [ + "scope_pattern" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "pattern_flow", + "line": 85, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "typed_parameter", + "line": 88, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "cast_receiver", + "line": 91, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "field_chain", + "line": 94, + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "array_receiver", + "line": 97, + "expectedFields": [ + "scope_items", + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "super_field", + "line": 100, + "expectedFields": [ + "base_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declared_receiver", + "line": 103, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "hiding_receiver", + "line": 106, + "expectedFields": [ + "shadow_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "generic_bound", + "line": 109, + "expectedFields": [ + "cell_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "static_fields", + "line": 112, + "expectedFields": [ + "scope_static", + "scope_static" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "method_not_field", + "line": 116, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declarator_order", + "line": 119, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "declarator_shadow", + "line": 120, + "expectedFields": [], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "inner_own", + "line": 125, + "expectedFields": [ + "inner_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "qualified_this", + "line": 128, + "expectedFields": [ + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "inherited_beats_outer", + "line": 133, + "expectedFields": [ + "base_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "anonymous_this", + "line": 140, + "expectedFields": [ + "anonymous_value", + "scope_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + }, + { + "id": "local_class_this", + "line": 148, + "expectedFields": [ + "local_value" + ], + "observedUniqueTargets": [], + "connectingRecords": [], + "targetOccurrencesMatched": 0, + "unexpectedTargets": [] + } + ], + "summary": { + "fieldDeclarations": 14, + "uniqueFields": 0, + "missingFields": 14, + "ambiguousFields": 0, + "fieldContactRecords": 0, + "registeredFieldOccurrences": 45, + "occurrenceTargetsMatched": 0, + "negativeCases": 8, + "negativeCasesWithContact": 0, + "callerOwnershipScored": false, + "edgePrecision": null + } + } + } +} diff --git a/benchmarks/agent_query/java_state_scope_oracle.json b/benchmarks/agent_query/java_state_scope_oracle.json new file mode 100644 index 000000000..f73d78ef0 --- /dev/null +++ b/benchmarks/agent_query/java_state_scope_oracle.json @@ -0,0 +1,1637 @@ +{ + "schema": "compass.java-state-scope-oracle/1", + "registrationSha256": "3a12b108736b5341ed09f0f9c66aa874e2612ef8d8e26afc542692fe0bdf445a", + "scriptSha256": "9a7f160a8c2a4a4ae9ed1c33d978aee953f2b9aafe9b8467d5b141a220272123", + "supportSha256": { + "runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "state_access_audit.py": "2bf95b707f18f34c9df4461a26f011b83d86f166efacfaab652f8e6231161058" + }, + "captureSha256": "ffeb55f94c91af8462243adbfea94b5e4a0c9788ebbd46233a3c48707197de32", + "compiler": "IMPLEMENTOR=\"Amazon.com Inc.\"\nIMPLEMENTOR_VERSION=\"Corretto-17.0.8.8.1\"\nJAVA_RUNTIME_VERSION=\"17.0.8.1+8-LTS\"\nJAVA_VERSION=\"17.0.8.1\"\nJAVA_VERSION_DATE=\"2023-08-22\"\nLIBC=\"default\"\nMODULES=\"java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.foreign jdk.incubator.vector jdk.internal.le jdk.internal.opt jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom jdk.zipfs\"\nOS_ARCH=\"aarch64\"\nOS_NAME=\"Darwin\"\nSOURCE=\".:git:9a3cc984f76c+\"\n", + "classes": { + "audit.Base": { + "owner": "audit.Base", + "parent": "java.lang.Object", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + } + ], + "major": 61, + "minor": 0 + }, + "audit.Cell": { + "owner": "audit.Cell", + "parent": "java.lang.Object", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + } + ], + "major": 61, + "minor": 0 + }, + "audit.ScopeFixture$1": { + "owner": "audit.ScopeFixture$1", + "parent": "java.lang.Object", + "interfaces": [ + "java.util.function.IntSupplier" + ], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + }, + { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true + } + ], + "major": 61, + "minor": 0 + }, + "audit.ScopeFixture$1Local": { + "owner": "audit.ScopeFixture$1Local", + "parent": "java.lang.Object", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + }, + { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true + } + ], + "major": 61, + "minor": 0 + }, + "audit.ScopeFixture$Inherited": { + "owner": "audit.ScopeFixture$Inherited", + "parent": "audit.Base", + "interfaces": [], + "fields": [ + { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true + } + ], + "major": 61, + "minor": 0 + }, + "audit.ScopeFixture$Inner": { + "owner": "audit.ScopeFixture$Inner", + "parent": "java.lang.Object", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + }, + { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true + } + ], + "major": 61, + "minor": 0 + }, + "audit.ScopeFixture": { + "owner": "audit.ScopeFixture", + "parent": "audit.Base", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + }, + { + "name": "slot", + "descriptor": "Laudit/Cell;", + "synthetic": false + }, + { + "name": "items", + "descriptor": "[Laudit/Cell;", + "synthetic": false + }, + { + "name": "failure", + "descriptor": "Ljava/lang/RuntimeException;", + "synthetic": false + }, + { + "name": "resource", + "descriptor": "Laudit/Token;", + "synthetic": false + }, + { + "name": "pattern", + "descriptor": "Ljava/lang/Object;", + "synthetic": false + }, + { + "name": "staticValue", + "descriptor": "I", + "synthetic": false + } + ], + "major": 61, + "minor": 0 + }, + "audit.Shadow": { + "owner": "audit.Shadow", + "parent": "audit.Cell", + "interfaces": [], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + } + ], + "major": 61, + "minor": 0 + }, + "audit.Token": { + "owner": "audit.Token", + "parent": "java.lang.Object", + "interfaces": [ + "java.lang.AutoCloseable" + ], + "fields": [ + { + "name": "value", + "descriptor": "I", + "synthetic": false + } + ], + "major": 61, + "minor": 0 + } + }, + "summary": { + "cases": 44, + "agreed": 44, + "positiveCases": 36, + "negativeCases": 8, + "fieldOccurrences": 45 + }, + "cases": [ + { + "id": "constructor", + "line": 17, + "text": " this.value = value; // case constructor: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 6, + "opcode": "putfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 6: putfield #7 // Field value:I", + "line": 17, + "method": "audit.ScopeFixture(int);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "explicit_this", + "line": 20, + "text": " return this.value; // case explicit_this: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 20, + "method": "int explicit();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "implicit_this", + "line": 23, + "text": " return value; // case implicit_this: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 23, + "method": "int implicit();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "repeated", + "line": 26, + "text": " return value + this.value; // case repeated: scope_value,scope_value", + "expectedFields": [ + "scope_value", + "scope_value" + ], + "observedFields": [ + "scope_value", + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 26, + "method": "int repeated();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + }, + { + "offset": 5, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 5: getfield #7 // Field value:I", + "line": 26, + "method": "int repeated();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "parameter_shadow", + "line": 29, + "text": " return value; // case parameter_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "block_before", + "line": 32, + "text": " sink(value); // case block_before: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 32, + "method": "void block();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "block_shadow", + "line": 35, + "text": " sink(value); // case block_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "block_after", + "line": 37, + "text": " sink(value); // case block_after: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 15, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 15: getfield #7 // Field value:I", + "line": 37, + "method": "void block();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "before_local", + "line": 40, + "text": " sink(value); // case before_local: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 40, + "method": "int beforeLocal();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "after_local", + "line": 42, + "text": " return value; // case after_local: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "for_shadow", + "line": 46, + "text": " sink(value); // case for_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "for_after", + "line": 48, + "text": " sink(value); // case for_after: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 18, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 18: getfield #7 // Field value:I", + "line": 48, + "method": "void loop();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "enhanced_iterable", + "line": 51, + "text": " for (Cell slot : items) { // case enhanced_iterable: scope_items", + "expectedFields": [ + "scope_items" + ], + "observedFields": [ + "scope_items" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "items", + "descriptor": "[Laudit/Cell;", + "raw": " 1: getfield #17 // Field items:[Laudit/Cell;", + "line": 51, + "method": "int enhanced();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "items", + "descriptor": "[Laudit/Cell;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_items" + } + ], + "agrees": true + }, + { + "id": "enhanced_binding", + "line": 52, + "text": " sink(slot.value); // case enhanced_binding: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 22, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 22: getfield #21 // Field audit/Cell.value:I", + "line": 52, + "method": "int enhanced();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "enhanced_after", + "line": 54, + "text": " return slot.value; // case enhanced_after: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedFields": [ + "scope_slot", + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 35, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "slot", + "descriptor": "Laudit/Cell;", + "raw": " 35: getfield #24 // Field slot:Laudit/Cell;", + "line": 54, + "method": "int enhanced();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "slot", + "descriptor": "Laudit/Cell;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_slot" + }, + { + "offset": 38, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 38: getfield #21 // Field audit/Cell.value:I", + "line": 54, + "method": "int enhanced();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "lambda_shadow", + "line": 57, + "text": " IntUnaryOperator a = value -> value + 1; // case lambda_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "lambda_this", + "line": 58, + "text": " IntSupplier b = () -> this.value; // case lambda_this: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 58, + "method": "private int lambda$lambdas$1();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "lambda_implicit", + "line": 59, + "text": " IntSupplier c = () -> value; // case lambda_implicit: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 59, + "method": "private int lambda$lambdas$2();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "catch_shadow", + "line": 65, + "text": " sink(failure == null ? 0 : 1); // case catch_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "catch_after", + "line": 67, + "text": " sink(failure == null ? 0 : 1); // case catch_after: scope_failure", + "expectedFields": [ + "scope_failure" + ], + "observedFields": [ + "scope_failure" + ], + "compilerInstructions": [ + { + "offset": 22, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "failure", + "descriptor": "Ljava/lang/RuntimeException;", + "raw": " 22: getfield #50 // Field failure:Ljava/lang/RuntimeException;", + "line": 67, + "method": "void catches();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "failure", + "descriptor": "Ljava/lang/RuntimeException;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_failure" + } + ], + "agrees": true + }, + { + "id": "resource_binding", + "line": 71, + "text": " sink(resource.value); // case resource_binding: token_value", + "expectedFields": [ + "token_value" + ], + "observedFields": [ + "token_value" + ], + "compilerInstructions": [ + { + "offset": 9, + "opcode": "getfield", + "owner": "audit.Token", + "name": "value", + "descriptor": "I", + "raw": " 9: getfield #57 // Field audit/Token.value:I", + "line": 71, + "method": "void resources();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Token" + }, + "field": "token_value" + } + ], + "agrees": true + }, + { + "id": "resource_catch", + "line": 73, + "text": " sink(resource.value); // case resource_catch: scope_resource,token_value", + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedFields": [ + "scope_resource", + "token_value" + ], + "compilerInstructions": [ + { + "offset": 43, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "resource", + "descriptor": "Laudit/Token;", + "raw": " 43: getfield #67 // Field resource:Laudit/Token;", + "line": 73, + "method": "void resources();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "resource", + "descriptor": "Laudit/Token;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_resource" + }, + { + "offset": 46, + "opcode": "getfield", + "owner": "audit.Token", + "name": "value", + "descriptor": "I", + "raw": " 46: getfield #57 // Field audit/Token.value:I", + "line": 73, + "method": "void resources();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Token" + }, + "field": "token_value" + } + ], + "agrees": true + }, + { + "id": "resource_after", + "line": 75, + "text": " sink(resource.value); // case resource_after: scope_resource,token_value", + "expectedFields": [ + "scope_resource", + "token_value" + ], + "observedFields": [ + "scope_resource", + "token_value" + ], + "compilerInstructions": [ + { + "offset": 53, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "resource", + "descriptor": "Laudit/Token;", + "raw": " 53: getfield #67 // Field resource:Laudit/Token;", + "line": 75, + "method": "void resources();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "resource", + "descriptor": "Laudit/Token;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_resource" + }, + { + "offset": 56, + "opcode": "getfield", + "owner": "audit.Token", + "name": "value", + "descriptor": "I", + "raw": " 56: getfield #57 // Field audit/Token.value:I", + "line": 75, + "method": "void resources();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Token" + }, + "field": "token_value" + } + ], + "agrees": true + }, + { + "id": "pattern_true", + "line": 79, + "text": " return slot.value; // case pattern_true: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 13, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 13: getfield #21 // Field audit/Cell.value:I", + "line": 79, + "method": "int pattern(java.lang.Object);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "pattern_after", + "line": 81, + "text": " return slot.value; // case pattern_after: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedFields": [ + "scope_slot", + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 18, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "slot", + "descriptor": "Laudit/Cell;", + "raw": " 18: getfield #24 // Field slot:Laudit/Cell;", + "line": 81, + "method": "int pattern(java.lang.Object);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "slot", + "descriptor": "Laudit/Cell;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_slot" + }, + { + "offset": 21, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 21: getfield #21 // Field audit/Cell.value:I", + "line": 81, + "method": "int pattern(java.lang.Object);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "pattern_input", + "line": 84, + "text": " if (!(pattern instanceof Cell slot)) return 0; // case pattern_input: scope_pattern", + "expectedFields": [ + "scope_pattern" + ], + "observedFields": [ + "scope_pattern" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "pattern", + "descriptor": "Ljava/lang/Object;", + "raw": " 1: getfield #71 // Field pattern:Ljava/lang/Object;", + "line": 84, + "method": "int negatedPattern();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "pattern", + "descriptor": "Ljava/lang/Object;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_pattern" + } + ], + "agrees": true + }, + { + "id": "pattern_flow", + "line": 85, + "text": " return slot.value; // case pattern_flow: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 23, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 23: getfield #21 // Field audit/Cell.value:I", + "line": 85, + "method": "int negatedPattern();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "typed_parameter", + "line": 88, + "text": " return cell.value; // case typed_parameter: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #21 // Field audit/Cell.value:I", + "line": 88, + "method": "int typed(audit.Cell);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "cast_receiver", + "line": 91, + "text": " return ((Cell) cell).value; // case cast_receiver: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 4, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 4: getfield #21 // Field audit/Cell.value:I", + "line": 91, + "method": "int cast(java.lang.Object);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "field_chain", + "line": 94, + "text": " return slot.value; // case field_chain: scope_slot,cell_value", + "expectedFields": [ + "scope_slot", + "cell_value" + ], + "observedFields": [ + "scope_slot", + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "slot", + "descriptor": "Laudit/Cell;", + "raw": " 1: getfield #24 // Field slot:Laudit/Cell;", + "line": 94, + "method": "int chain();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "slot", + "descriptor": "Laudit/Cell;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_slot" + }, + { + "offset": 4, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 4: getfield #21 // Field audit/Cell.value:I", + "line": 94, + "method": "int chain();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "array_receiver", + "line": 97, + "text": " return items[0].value; // case array_receiver: scope_items,cell_value", + "expectedFields": [ + "scope_items", + "cell_value" + ], + "observedFields": [ + "scope_items", + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "items", + "descriptor": "[Laudit/Cell;", + "raw": " 1: getfield #17 // Field items:[Laudit/Cell;", + "line": 97, + "method": "int indexed();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "items", + "descriptor": "[Laudit/Cell;", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_items" + }, + { + "offset": 6, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 6: getfield #21 // Field audit/Cell.value:I", + "line": 97, + "method": "int indexed();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "super_field", + "line": 100, + "text": " return super.value; // case super_field: base_value", + "expectedFields": [ + "base_value" + ], + "observedFields": [ + "base_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.Base", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #75 // Field audit/Base.value:I", + "line": 100, + "method": "int parent();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Base" + }, + "field": "base_value" + } + ], + "agrees": true + }, + { + "id": "declared_receiver", + "line": 103, + "text": " return cell.value; // case declared_receiver: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #21 // Field audit/Cell.value:I", + "line": 103, + "method": "int staticBinding(audit.Cell, audit.Shadow);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "hiding_receiver", + "line": 106, + "text": " return cell.value; // case hiding_receiver: shadow_value", + "expectedFields": [ + "shadow_value" + ], + "observedFields": [ + "shadow_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.Shadow", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #76 // Field audit/Shadow.value:I", + "line": 106, + "method": "int subtype(audit.Shadow);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Shadow" + }, + "field": "shadow_value" + } + ], + "agrees": true + }, + { + "id": "generic_bound", + "line": 109, + "text": " return cell.value; // case generic_bound: cell_value", + "expectedFields": [ + "cell_value" + ], + "observedFields": [ + "cell_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.Cell", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #21 // Field audit/Cell.value:I", + "line": 109, + "method": " int generic(T);", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Cell" + }, + "field": "cell_value" + } + ], + "agrees": true + }, + { + "id": "static_fields", + "line": 112, + "text": " return staticValue + ScopeFixture.staticValue; // case static_fields: scope_static,scope_static", + "expectedFields": [ + "scope_static", + "scope_static" + ], + "observedFields": [ + "scope_static", + "scope_static" + ], + "compilerInstructions": [ + { + "offset": 0, + "opcode": "getstatic", + "owner": "audit.ScopeFixture", + "name": "staticValue", + "descriptor": "I", + "raw": " 0: getstatic #79 // Field staticValue:I", + "line": 112, + "method": "static int statics();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "staticValue", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_static" + }, + { + "offset": 3, + "opcode": "getstatic", + "owner": "audit.ScopeFixture", + "name": "staticValue", + "descriptor": "I", + "raw": " 3: getstatic #79 // Field staticValue:I", + "line": 112, + "method": "static int statics();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "staticValue", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_static" + } + ], + "agrees": true + }, + { + "id": "method_not_field", + "line": 116, + "text": " return value(); // case method_not_field: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "declarator_order", + "line": 119, + "text": " int first = value, value = 2; // case declarator_order: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #7 // Field value:I", + "line": 119, + "method": "void declarators();", + "bytecodeClass": "audit.ScopeFixture", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "declarator_shadow", + "line": 120, + "text": " sink(first + value); // case declarator_shadow: -", + "expectedFields": [], + "observedFields": [], + "compilerInstructions": [], + "agrees": true + }, + { + "id": "inner_own", + "line": 125, + "text": " return value; // case inner_own: inner_value", + "expectedFields": [ + "inner_value" + ], + "observedFields": [ + "inner_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture$Inner", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #13 // Field value:I", + "line": 125, + "method": "int own();", + "bytecodeClass": "audit.ScopeFixture$Inner", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture$Inner" + }, + "field": "inner_value" + } + ], + "agrees": true + }, + { + "id": "qualified_this", + "line": 128, + "text": " return ScopeFixture.this.value; // case qualified_this: scope_value", + "expectedFields": [ + "scope_value" + ], + "observedFields": [ + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture$Inner", + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "raw": " 1: getfield #1 // Field this$0:Laudit/ScopeFixture;", + "line": 128, + "method": "int outer();", + "bytecodeClass": "audit.ScopeFixture$Inner", + "declaration": { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true, + "owner": "audit.ScopeFixture$Inner" + }, + "field": null + }, + { + "offset": 4, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 4: getfield #17 // Field audit/ScopeFixture.value:I", + "line": 128, + "method": "int outer();", + "bytecodeClass": "audit.ScopeFixture$Inner", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "inherited_beats_outer", + "line": 133, + "text": " return value; // case inherited_beats_outer: base_value", + "expectedFields": [ + "base_value" + ], + "observedFields": [ + "base_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture$Inherited", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #13 // Field value:I", + "line": 133, + "method": "int inherited();", + "bytecodeClass": "audit.ScopeFixture$Inherited", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.Base" + }, + "field": "base_value" + } + ], + "agrees": true + }, + { + "id": "anonymous_this", + "line": 140, + "text": " return this.value + ScopeFixture.this.value; // case anonymous_this: anonymous_value,scope_value", + "expectedFields": [ + "anonymous_value", + "scope_value" + ], + "observedFields": [ + "anonymous_value", + "scope_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture$1", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #13 // Field value:I", + "line": 140, + "method": "public int getAsInt();", + "bytecodeClass": "audit.ScopeFixture$1", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture$1" + }, + "field": "anonymous_value" + }, + { + "offset": 5, + "opcode": "getfield", + "owner": "audit.ScopeFixture$1", + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "raw": " 5: getfield #1 // Field this$0:Laudit/ScopeFixture;", + "line": 140, + "method": "public int getAsInt();", + "bytecodeClass": "audit.ScopeFixture$1", + "declaration": { + "name": "this$0", + "descriptor": "Laudit/ScopeFixture;", + "synthetic": true, + "owner": "audit.ScopeFixture$1" + }, + "field": null + }, + { + "offset": 8, + "opcode": "getfield", + "owner": "audit.ScopeFixture", + "name": "value", + "descriptor": "I", + "raw": " 8: getfield #17 // Field audit/ScopeFixture.value:I", + "line": 140, + "method": "public int getAsInt();", + "bytecodeClass": "audit.ScopeFixture$1", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture" + }, + "field": "scope_value" + } + ], + "agrees": true + }, + { + "id": "local_class_this", + "line": 148, + "text": " return this.value; // case local_class_this: local_value", + "expectedFields": [ + "local_value" + ], + "observedFields": [ + "local_value" + ], + "compilerInstructions": [ + { + "offset": 1, + "opcode": "getfield", + "owner": "audit.ScopeFixture$1Local", + "name": "value", + "descriptor": "I", + "raw": " 1: getfield #13 // Field value:I", + "line": 148, + "method": "int get();", + "bytecodeClass": "audit.ScopeFixture$1Local", + "declaration": { + "name": "value", + "descriptor": "I", + "synthetic": false, + "owner": "audit.ScopeFixture$1Local" + }, + "field": "local_value" + } + ], + "agrees": true + } + ] +} diff --git a/benchmarks/agent_query/tests/test_java_state_scope.py b/benchmarks/agent_query/tests/test_java_state_scope.py new file mode 100644 index 000000000..06a60ebb7 --- /dev/null +++ b/benchmarks/agent_query/tests/test_java_state_scope.py @@ -0,0 +1,197 @@ +import copy +import json +from pathlib import Path +import struct +import tempfile +import unittest + +from benchmarks.agent_query.java_state_scope_audit import ( + ClassReader, field_declaration, graph_inventory, parse_javap, registration, +) + + +DISASSEMBLY = '''Compiled from "Fixture.java" +class audit.Box { + int value; + descriptor: I + + int get(); + descriptor: ()I + Code: + 0: aload_0 + 1: getfield #7 // Field value:I + 4: aload_0 + 5: getfield #7 // Field audit/Other.value:I + 8: iadd + 9: ireturn + LineNumberTable: + line 10: 0 + line 11: 4 + + private int lambda$get$0(); + descriptor: ()I + Code: + 0: aload_0 + 1: getfield #7 // Field value:I + 4: ireturn + LineNumberTable: + line 12: 0 +} +''' + + +def cls(parent=None, fields=(), interfaces=()): + return dict(parent=parent, interfaces=list(interfaces), fields=[ + dict(name=name, descriptor='I', synthetic=False) for name in fields]) + + +class JavaScopeTests(unittest.TestCase): + def test_registered_source_is_unchanged_and_all_targets_exist(self): + reg = registration(Path('benchmarks/agent_query/java_state_scope_registration.json')) + self.assertEqual(len(reg['cases']), 44) + self.assertEqual(sum(not c['expectedFields'] for c in reg['cases']), 8) + self.assertEqual(sum(len(c['expectedFields']) for c in reg['cases']), 45) + + def test_field_bytecodes_keep_offset_owner_and_lambda_method(self): + rows, covered = parse_javap(DISASSEMBLY, 'audit.Box') + self.assertEqual(covered, {10, 11, 12}) + self.assertEqual([(r['owner'], r['line'], r['offset']) for r in rows], + [('audit.Box', 10, 1), ('audit.Other', 11, 5), ('audit.Box', 12, 1)]) + self.assertIn('lambda$get$0', rows[-1]['method']) + + def test_multiplicity_and_repeated_source_line_are_preserved(self): + rows, _ = parse_javap(DISASSEMBLY.replace('line 11: 4', 'line 10: 4'), 'audit.Box') + self.assertEqual(sum(r['line'] == 10 for r in rows), 2) + + def test_field_hiding_uses_declared_bytecode_owner(self): + classes = {'Base': cls(fields=['value']), 'Sub': cls('Base', ['value']), 'Inner': cls('Base')} + self.assertEqual(field_declaration(classes, 'Base', 'value', 'I')['owner'], 'Base') + self.assertEqual(field_declaration(classes, 'Sub', 'value', 'I')['owner'], 'Sub') + self.assertEqual(field_declaration(classes, 'Inner', 'value', 'I')['owner'], 'Base') + + def test_ambiguous_missing_and_cyclic_fields_fail(self): + for classes, owner in [ + ({'A': cls('B'), 'B': cls('A')}, 'A'), + ({'A': cls()}, 'A'), + ({'A': cls(interfaces=['B', 'C']), 'B': cls(fields=['value']), 'C': cls(fields=['value'])}, 'A'), + ]: + with self.subTest(classes=classes), self.assertRaises(ValueError): + field_declaration(classes, owner, 'value', 'I') + + def test_missing_or_ambiguous_line_tables_fail(self): + for text in [DISASSEMBLY.replace(' line 10: 0\n', ''), + DISASSEMBLY.replace('line 11: 4', 'line 11: 0'), + DISASSEMBLY.replace('line 11: 4', 'line 11: 3')]: + with self.subTest(text=text), self.assertRaises(ValueError): + parse_javap(text, 'audit.Box') + + def test_unrecognized_field_instruction_is_not_dropped(self): + with self.assertRaises(ValueError): + parse_javap(DISASSEMBLY.replace('// Field value:I', '// Unexpected value:I'), 'audit.Box') + + def test_empty_or_unordered_bytecode_fails(self): + for text in ['', DISASSEMBLY.replace(' 5: getfield', ' 1: getfield')]: + with self.subTest(text=text), self.assertRaises(ValueError): + parse_javap(text, 'audit.Box') + + def test_class_metadata_and_synthetic_field_flags(self): + def utf(text): + raw = text.encode() + return b'\1' + struct.pack('>H', len(raw)) + raw + # Pool: class name, Class entry, field name, field descriptor. + data = (b'\xca\xfe\xba\xbe' + struct.pack('>HHH', 0, 61, 5) + + utf('audit/Box') + b'\7\0\1' + utf('this$0') + utf('Laudit/Outer;') + + struct.pack('>HHHHH', 0, 2, 0, 0, 1) + + struct.pack('>HHHH', 0x1010, 3, 4, 0)) + result = ClassReader(data).parse() + self.assertEqual(result['owner'], 'audit.Box') + self.assertTrue(result['fields'][0]['synthetic']) + self.assertEqual(result['major'], 61) + with self.assertRaises(ValueError): + ClassReader(data[:-1]).parse() + with self.assertRaises(ValueError): + ClassReader(b'bad!').parse() + + + def test_graph_inventory_never_scores_empty_negatives_as_precision(self): + reg = dict(source='Fixture.java', fields=dict(value=dict(owner='Box', name='value', line=2)), + cases=[dict(id='positive', line=5, expectedFields=['value']), + dict(id='negative', line=6, expectedFields=[])]) + graph = dict(directed=True, nodes=[dict(id='field', name='value', kind='field', + source=dict(file='Fixture.java', startLine=2))], links=[]) + result = graph_inventory(graph, 'compass', reg) + self.assertEqual(result['summary']['uniqueFields'], 1) + self.assertEqual(result['summary']['occurrenceTargetsMatched'], 0) + self.assertEqual(result['summary']['negativeCasesWithContact'], 0) + self.assertIsNone(result['summary']['edgePrecision']) + self.assertFalse(result['summary']['callerOwnershipScored']) + + def test_graph_inventory_retains_duplicates_and_wrong_targets(self): + reg = dict(source='Fixture.java', fields=dict(value=dict(name='value', line=2)), + cases=[dict(id='positive', line=5, expectedFields=['value']), + dict(id='negative', line=6, expectedFields=[])]) + graph = dict(directed=True, nodes=[ + dict(id='field', name='value', kind='field', source=dict(file='Fixture.java', startLine=2)), + dict(id='method', name='get', kind='method', source=dict(file='Fixture.java', startLine=4))], links=[ + dict(id='one', source='method', target='field', kind='references', + relationshipSite=dict(file='Fixture.java', startLine=5)), + dict(id='two', source='method', target='field', kind='references', + relationshipSite=dict(file='Fixture.java', startLine=5)), + dict(id='bad', source='method', target='field', kind='references', + relationshipSite=dict(file='Fixture.java', startLine=6))]) + result = graph_inventory(graph, 'compass', reg) + self.assertEqual(len(result['allContactRecords']), 3) + self.assertEqual(result['summary']['occurrenceTargetsMatched'], 1) + self.assertEqual(result['summary']['negativeCasesWithContact'], 1) + self.assertEqual(result['cases'][0]['unexpectedTargets'], ['value']) + self.assertEqual(result['cases'][1]['unexpectedTargets'], ['value']) + graph['nodes'].append(dict(graph['nodes'][0], id='duplicate')) + result = graph_inventory(graph, 'compass', reg) + self.assertEqual(result['summary']['ambiguousFields'], 1) + self.assertEqual(result['summary']['occurrenceTargetsMatched'], 0) + + def test_graphify_direction_and_absent_occurrence_remain_visible(self): + reg = dict(source='Fixture.java', fields=dict(value=dict(name='value', line=2)), + cases=[dict(id='positive', line=5, expectedFields=['value'])]) + graph = dict(directed=False, nodes=[ + dict(id='f', label='value', source_file='Fixture.java', source_location='L2'), + dict(id='m', label='get', source_file='Fixture.java', source_location='L4')], links=[ + dict(source='m', target='f', relation='references')]) + result = graph_inventory(graph, 'graphify', reg) + self.assertFalse(result['graphDirected']) + self.assertEqual(result['summary']['fieldContactRecords'], 1) + self.assertEqual(result['summary']['occurrenceTargetsMatched'], 0) + graph['links'][0].update(source_file='Fixture.java', source_location='L5') + self.assertEqual(graph_inventory(graph, 'graphify', reg)['summary']['occurrenceTargetsMatched'], 1) + graph['links'][0].update(source='f', target='m') + self.assertEqual(graph_inventory(graph, 'graphify', reg)['summary']['occurrenceTargetsMatched'], 0) + + def test_invalid_graph_identity_fails(self): + reg = dict(source='Fixture.java', fields={}, cases=[]) + for graph in [dict(nodes=[dict(id='x'), dict(id='x')], links=[]), + dict(nodes=[dict(id='x')], links=[dict(source='x', target='missing')])]: + with self.subTest(graph=graph), self.assertRaises(ValueError): + graph_inventory(graph, 'compass', reg) + + + def test_registration_refuses_changed_source_or_unknown_fields(self): + original = json.loads(Path('benchmarks/agent_query/java_state_scope_registration.json').read_text()) + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / 'registration.json' + for mutation in ['hash', 'target', 'duplicate', 'line']: + data = copy.deepcopy(original) + if mutation == 'hash': + data['sourceSha256'] = 'wrong' + elif mutation == 'target': + data['cases'][0]['expectedFields'] = ['invented'] + elif mutation == 'duplicate': + data['cases'].append(data['cases'][0]) + else: + data['cases'][0]['text'] = 'wrong' + path.write_text(json.dumps(data)) + with self.subTest(mutation=mutation), self.assertRaises(ValueError): + registration(path) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 4d840dec8..82553236e 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2876,6 +2876,84 @@ verifier and validation logs are under `rust-state-access-01`. Remaining Java, TypeScript, Go and Python state evidence, actual cohesion/god-object judgments, authored explanations and fresh held-out confirmation remain unfinished. +## Java field-scope compiler oracle and baseline + +Before changing Java extraction, commit `d1b701b5` registered a 44-case source +fixture. The prior five-repository registration remains unchanged. This is a +synthetic development challenge motivated by the four missing jsoup accesses, +not a fresh repository sample or a held-out score. + +The existing Java `java_value_types` map is keyed by declaration scope and name; +local declarations are inserted without block lifetimes. Reusing it for field +access would lose distinctions required by Java name scope. Local variables, +loop bindings, resources and catch parameters have different scope boundaries; +pattern scope depends on flow. Field selection also follows the receiver's +compile-time type. These rules are specified in +[JLS scope](https://docs.oracle.com/javase/specs/jls/se21/html/jls-6.html#jls-6.3) +and [field access](https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.11). + +The installed Amazon Corretto 17.0.8 compiler was invoked with `--release 17`, +`-proc:none`, `-implicit:none` and source/line/local-variable debug tables. +Fixture code was never executed. A bounded class-file reader records declared +fields, descriptors, synthetic flags and superclass/interface links. Captured +`javap -p -c -l -s` output supplies field instructions and source-line tables, +including compiler-generated lambda bodies. Registered expectations agree with +all **44 cases: 36 positive cases, eight negative cases, 45 field occurrences**. +Repeated fields on one line retain multiplicity. Inherited bytecode owners are +resolved to the actual declaring field; enclosing `this$0` instructions remain +visible but their compiler-marked synthetic targets are not source fields. + +Fresh native Compass and Graphify captures use the identical registered source: + +| Evidence | Compass | Graphify | +| --- | ---: | ---: | +| Graph nodes / edges | 53 / 78 | 43 / 62 | +| Registered source field declarations | 12/14 | 0/14 | +| Contacts to any registered field | 0 | 0 | +| Registered occurrence targets recovered | 0/45 | 0/45 | +| Negative lines with a registered field contact | 0/8 | 0/8 | + +Compass's absent declarations are the anonymous-class and local-class fields. +The compiler challenge also proves targets for resource-scope exit, flow-scoped +patterns, inherited fields hiding enclosing fields, typed/cast/array receivers, +and distinct lambda versus anonymous-class `this`. These must be explicit +coverage requirements or documented unsupported cases in the correction; +name-only fallback is not acceptable. + +This inventory does not score source-caller ownership, read/write classification, +positive edge precision, complete Java coverage, cohesion or god-object defects. +With no positive contacts, the empty negative controls provide no positive +precision evidence. Constant folding and arbitrary compiler desugarings make +bytecode unsuitable as an unrestricted source-occurrence oracle; this fixture +uses nonconstant fields and fails when its source inventory, source-line +coverage or expected multiplicity disagrees. No existing real-repository score +is increased by these synthetic results. + +The Compass executable is the same hashed binary as the previous qualified +Rust-field run. Graphify 0.9.67 uses its unmodified installed native extractor; +its existing stale skill-installation warning is retained, and no skill was +installed or used. Graphify's undirected container flag is preserved. This is +an extraction/representation diagnostic, not a build-speed or public-query +comparison. + +`java_state_scope_audit.py` supplies bounded capture and deterministic offline +replay. The 14 new tests exercise bytecode offsets and line boundaries, +multiplicity, lambdas, hiding, hierarchy ambiguity/cycles, synthetic flags, +source drift, missing/duplicate graph endpoints, wrong targets and absent edge +anchors. All **162 benchmark tests pass**. The saved compiler and graph reviews +replay byte-for-byte, and package/binary/source/capture hashes are checked. +No production Rust or JavaScript implementation changed in this checkpoint; +native Rust, browser and packaging gates were not rerun. Java production +field-access extraction and the unchanged five-repository rerun remain next. + +Committed artifacts are `java_state_scope_registration.json`, +`java_state_scope_oracle.json` and `java_state_scope_baseline.json` under +`benchmarks/agent_query`; complete compiler/graph captures, exploratory reports +and verification logs are under external `java-state-scope-01`. The registration +precedes compiler and graph observations. The graph inventory explicitly reports +unscored caller ownership rather than treating a matching target/line as a full +semantic-edge judgment. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 5c10c9d43c362dc3e8d3b00ef5dd031eccfb1d81 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:40:45 -0700 Subject: [PATCH 83/97] audit: register Java field-access correction and unchanged comparisons --- ...a_state_access_development_registration.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 benchmarks/agent_query/java_state_access_development_registration.json diff --git a/benchmarks/agent_query/java_state_access_development_registration.json b/benchmarks/agent_query/java_state_access_development_registration.json new file mode 100644 index 000000000..88213b22a --- /dev/null +++ b/benchmarks/agent_query/java_state_access_development_registration.json @@ -0,0 +1,17 @@ +{ + "schema": "compass.java-state-access-development-registration/1", + "baselineCommit": "716446141d8c1e9e0d945487b041d03f5e72e423", + "sourceRegistration": "benchmarks/agent_query/state_access_development_registration.json", + "sourceRegistrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "scopeRegistration": "benchmarks/agent_query/java_state_scope_registration.json", + "scopeRegistrationSha256": "3a12b108736b5341ed09f0f9c66aa874e2612ef8d8e26afc542692fe0bdf445a", + "scope": "Java production field-access correction on known development subjects. Retain all 44 compiler scope cases and all 20 five-repository access sites; unsupported cases remain failures. Not held-out or god-object classification.", + "contract": "Emit exact field-only member-access occurrences using lexical value bindings and source-proven receiver types. Scope exit restores outer bindings; unsupported/ambiguous receivers never select an outer or same-named field. Keep qualified universal resolution and existing graph References projection. Preserve occurrences, method-selector exclusion and source anchors. Bounded inference; source/compiler limitations remain explicit.", + "evaluation": "Rebuild all five Compass graphs from the same pinned read-only sources; reuse unchanged Graphify graphs. Replay the unchanged 20-site auditor. Rebuild the compiler fixture and compare all 44 cases against the saved compiler oracle, retaining all extra/missing targets. Report complete graph deltas and community changes. Inspect every newly published jsoup field occurrence for field target/source-anchor consistency; this does not prove compiler-grade precision.", + "verification": [ + "Fail-before native regressions, lexical scope and ambiguous/unknown negatives", + "Cross-file resolution and publication tests with exact caller, field, direction, multiplicity and byte anchors", + "Cache invalidation and native baseline; production fixture qualification", + "Compiler scope fixture rerun and full five-repository state diagnostic with exact source/binary/graph hashes" + ] +} From 09ca58e6a92ee6ec5045e7cbde14300d136b9e6a Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:53:44 -0700 Subject: [PATCH 84/97] Emit Java field-access evidence with bounded lexical receiver scopes --- .github/workflows/compass-ci.yml | 3 + CHANGELOG.md | 4 + COMPATIBILITY.md | 24 + MIGRATION.md | 7 + crates/compass-files/src/cache.rs | 2 +- crates/compass-files/tests/contracts.rs | 6 + .../compass-languages/src/evidence/build.rs | 6 + .../src/evidence/build/java_fields.rs | 796 ++++++++++++++++++ .../tests/java_field_access.rs | 224 +++++ .../tests/java_field_access.rs | 151 ++++ docs/reference/universal-semantic-evidence.md | 21 + 11 files changed, 1243 insertions(+), 1 deletion(-) create mode 100644 crates/compass-languages/src/evidence/build/java_fields.rs create mode 100644 crates/compass-languages/tests/java_field_access.rs create mode 100644 crates/compass-resolve/tests/java_field_access.rs diff --git a/.github/workflows/compass-ci.yml b/.github/workflows/compass-ci.yml index a13693c74..c0180f072 100644 --- a/.github/workflows/compass-ci.yml +++ b/.github/workflows/compass-ci.yml @@ -113,6 +113,9 @@ jobs: - name: Rust state-access evidence contracts run: cargo test -p compass-languages -p compass-resolve --test rust_field_access --locked + - name: Java state-access evidence contracts + run: cargo test -p compass-languages -p compass-resolve --test java_field_access --locked + - name: Comparison scorer regressions (no competitor installation) run: python3 -m unittest discover -s benchmarks/agent_query/tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 88055476e..3a8b33932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Emit Java field-access references from bounded lexical scope and receiver + evidence, preserving shadowing, field identity and parallel source occurrences. + Invalidate prior AST caches; retain unsupported targets without name fallback. + - Emit Rust field-access evidence for source-proven nominal receivers, preserving exact occurrences and unknown or shadowed receiver outcomes. Publish qualified field contacts as references without inventing read/write effects. Rebuild diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index cf6204dcd..64a940789 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -31,6 +31,30 @@ not maintain command-specific fallbacks for older releases. Compass 0.3.0 itself remains supported. The extension adapts typed call-query results for the known nested-anchor limitation in that stable release. +## Java field-access evidence + +Java emits `MemberAccess` occurrences for ordinary field expressions and +unqualified source-declared fields. A bounded AST lexical index distinguishes +parameters, locals, block/loop lifetimes, lambda/catch/resource bindings and +source type names. Receiver typing supports declared nominal values, source-local +field chains, arrays, casts, direct constructors and single generic bounds. +Simple `instanceof` branches and abrupt guards retain their flow scope. + +Unknown receivers, ambiguous names, unsupported pattern flow, inherited fields, +unregistered local/anonymous class owners and exhausted inference remain +unresolved or unrepresented. Unsupported flow masks a possibly shadowed field; +it never establishes a convenient outer-field target. Cross-file field chains +and general hierarchy/accessibility/type-checking are not inferred. Existing +qualified universal resolution selects only field declarations. Graph v1 emits +`references` with exact member-access provenance, preserving occurrences without +read/write or runtime-alias claims. Method names are not field accesses. + +Rebuild graphs to obtain these facts. AST cache semantics advance from 8 to 9; +evidence/graph schemas, producer capabilities and package version are unchanged. +Historical realizations remain immutable. Additional references can change +navigation and community assignments; neither implies improved community +quality or god-object classification. + ## Rust field-access evidence Rust extraction emits member-access occurrences for explicit field expressions, diff --git a/MIGRATION.md b/MIGRATION.md index 8b01f3726..af925f554 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,13 @@ layout remains visible and clearly owned. ## Graph rebuilds and query resolution +Rebuild Java graphs to receive source-proven field-access references with lexical +scope and occurrence evidence. AST cache semantics version 9 automatically +invalidates earlier extraction facts. Existing stored/historical graphs are not +rewritten; unsupported receivers and inherited/anonymous/local-class cases +remain gaps. These references do not classify read/write effects or establish +class cohesion or god-object defects. + Rebuild Rust graphs to receive newly emitted field-access references. AST cache semantics version 8 invalidates earlier facts automatically; stored graphs and historical realizations are not rewritten. The added `member-access` references diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index 03d609cae..dd5c60b27 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -13,7 +13,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; /// Changes whenever cached extraction semantics change, even if the wire encoding does not. -pub const AST_CACHE_VERSION: &str = "8"; +pub const AST_CACHE_VERSION: &str = "9"; /// Portable cache encoding version used in the on-disk namespace. pub const CACHE_ENCODING_VERSION: u32 = 1; const MESSAGEPACK_EXTENSION: &str = "msgpack"; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index 1fb2f4c77..f4107634e 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -991,6 +991,11 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< fs::create_dir_all(cache_root.join("compass-out/cache/ast/v3/e1"))?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/v5/e1"))?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/v7/e1"))?; + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v8/e1"))?; + fs::write( + cache_root.join("compass-out/cache/ast/v8/e1/stale.msgpack"), + "Java facts without lexical field-access occurrences", + )?; fs::write( cache_root.join("compass-out/cache/ast/v7/e1/stale.msgpack"), "Rust facts without field-access occurrences", @@ -1038,6 +1043,7 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< assert!(!cache_root.join("compass-out/cache/ast/v3").exists()); assert!(!cache_root.join("compass-out/cache/ast/v5").exists()); assert!(!cache_root.join("compass-out/cache/ast/v7").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v8").exists()); let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( diff --git a/crates/compass-languages/src/evidence/build.rs b/crates/compass-languages/src/evidence/build.rs index bc5ebf0e8..99604cf10 100644 --- a/crates/compass-languages/src/evidence/build.rs +++ b/crates/compass-languages/src/evidence/build.rs @@ -15,6 +15,8 @@ use super::model::{ }; use super::validate::{EvidenceError, EvidenceErrorCode, EvidenceLimits, validate_evidence}; +mod java_fields; + // Go selector attribution can cross a closure, a multi-return call, and a // range expression before reaching the receiver type. Keep that traversal // bounded, but allow the real-world chain without falling back to an @@ -1049,6 +1051,7 @@ struct DirectEvidenceState<'source> { go_range_member_types: HashMap<(String, String), String>, java_containers: HashMap, java_value_types: HashMap>, + java_fields: java_fields::JavaFieldIndex, graph_ids: HashSet, parser_error_ranges: Vec<(usize, usize)>, builder: EvidenceBuilder, @@ -1143,6 +1146,7 @@ impl<'source> DirectEvidenceState<'source> { go_range_member_types: HashMap::new(), java_containers: HashMap::new(), java_value_types: HashMap::new(), + java_fields: java_fields::JavaFieldIndex::default(), graph_ids: HashSet::new(), parser_error_ranges: Vec::new(), builder: EvidenceBuilder::new( @@ -2864,6 +2868,7 @@ impl<'source> DirectEvidenceState<'source> { self.collect_java_imports(root, &file)?; self.collect_java_declarations(root, &file)?; self.collect_java_value_types(root, &file)?; + self.index_java_field_values(root, &file)?; self.walk_java_evidence(root, &file, true) } @@ -3441,6 +3446,7 @@ impl<'source> DirectEvidenceState<'source> { match node.kind() { "import_declaration" => return Ok(()), "method_invocation" => self.add_java_method_call(node, &active)?, + "field_access" | "identifier" => self.add_java_field_access(node, &active)?, "object_creation_expression" => self.add_java_construction(node, &active)?, _ => {} } diff --git a/crates/compass-languages/src/evidence/build/java_fields.rs b/crates/compass-languages/src/evidence/build/java_fields.rs new file mode 100644 index 000000000..6d6586055 --- /dev/null +++ b/crates/compass-languages/src/evidence/build/java_fields.rs @@ -0,0 +1,796 @@ +//! Java field evidence uses AST lexical ranges, not the method-wide call type map. +use super::*; + +const DEPTH: usize = 64; +const RECEIVER_DEPTH: usize = 16; + +#[derive(Clone)] +struct Local { + start: usize, + end: usize, + nominal: Option, +} + +#[derive(Clone)] +struct Field { + qualified: String, + nominal: Option, + is_static: bool, +} + +#[derive(Default)] +pub(super) struct JavaFieldIndex { + locals: HashMap>>, + local_types: HashMap>, + fields: HashMap>>, + bindings: usize, + static_scopes: HashSet, +} + +enum Value<'a> { + Local(Option<&'a str>), + Field(&'a Field), + Unknown, + Absent, +} + +fn ancestors(node: Node<'_>) -> impl Iterator> { + std::iter::successors(Some(node), |node| node.parent()).take(DEPTH) +} + +fn is_static(node: Node<'_>, source: &[u8]) -> bool { + if !matches!( + node.kind(), + "static_initializer" + | "field_declaration" + | "constant_declaration" + | "method_declaration" + | "constructor_declaration" + ) && java_container_kind(node.kind()).is_none() + { + return false; + } + node.kind() == "static_initializer" || { + let mut cursor = node.walk(); + node.named_children(&mut cursor) + .find(|child| child.kind() == "modifiers") + .is_some_and(|modifiers| { + let mut cursor = modifiers.walk(); + modifiers.children(&mut cursor).any(|child| { + child.kind() == "static" && child.utf8_text(source).ok() == Some("static") + }) + }) + } +} + +fn anonymous_body(node: Node<'_>) -> bool { + node.kind() == "class_body" + && node.parent().is_some_and(|parent| { + matches!( + parent.kind(), + "object_creation_expression" | "enum_constant" + ) + }) +} + +fn first_named(node: Node<'_>) -> Option> { + let mut cursor = node.walk(); + node.named_children(&mut cursor) + .find(|child| !child.is_extra()) +} + +impl DirectEvidenceState<'_> { + pub(super) fn index_java_field_values( + &mut self, + node: Node<'_>, + owner: &DeclarationContext, + ) -> Result<(), EvidenceError> { + if is_static(node, self.source) { + ensure_capacity( + "Java field lexical bindings", + self.java_fields.bindings, + self.builder.limits.bindings, + )?; + self.java_fields.bindings += 1; + self.java_fields.static_scopes.insert(node.id()); + } + let active = self + .declarations + .get(&node.id()) + .cloned() + .unwrap_or_else(|| owner.clone()); + if java_container_kind(node.kind()).is_some() + && !self.java_containers.contains_key(&node.id()) + && let (Some(parent), Some(name)) = (node.parent(), node.child_by_field_name("name")) + && matches!(parent.kind(), "block" | "constructor_body" | "switch_block") + { + ensure_capacity( + "Java field lexical bindings", + self.java_fields.bindings, + self.builder.limits.bindings, + )?; + self.java_fields.bindings += 1; + let spelling = self.text(name); + self.java_fields + .local_types + .entry(parent.id()) + .or_default() + .entry(spelling) + .and_modify(|start| *start = (*start).min(node.start_byte())) + .or_insert(node.start_byte()); + } + match node.kind() { + "variable_declarator" => { + if let (Some(parent), Some(name)) = + (node.parent(), node.child_by_field_name("name")) + { + if matches!(parent.kind(), "field_declaration" | "constant_declaration") { + if let Some(context) = self.declarations.get(&node.id()) { + let nominal = parent + .child_by_field_name("type") + .and_then(|ty| self.java_field_type(&active, ty, node, 0)); + let spelling = self.text(name); + self.java_fields + .fields + .entry( + context + .enclosing_type_qualified_name + .clone() + .unwrap_or_default(), + ) + .or_default() + .entry(spelling) + .or_default() + .push(Field { + qualified: context.qualified_name.clone(), + nominal, + is_static: parent.kind() == "constant_declaration" + || self.java_fields.static_scopes.contains(&parent.id()), + }); + } + } else if parent.kind() == "local_variable_declaration" + && let Some(scope) = ancestors(parent).skip(1).find(|ancestor| { + matches!( + ancestor.kind(), + "block" | "constructor_body" | "for_statement" | "switch_block" + ) + }) + { + let nominal = parent + .child_by_field_name("type") + .and_then(|ty| self.java_field_type(&active, ty, node, 0)); + self.java_field_local( + scope, + name, + name.start_byte(), + scope.end_byte(), + nominal, + )?; + } + } + } + "formal_parameter" | "spread_parameter" | "catch_formal_parameter" => { + if let Some(name) = java_parameter_declarator(node).child_by_field_name("name") + && let Some(scope) = ancestors(node).skip(1).find(|ancestor| { + matches!( + ancestor.kind(), + "method_declaration" + | "constructor_declaration" + | "lambda_expression" + | "catch_clause" + ) + }) + { + let nominal = java_parameter_type_node(node) + .or_else(|| first_named(node).filter(|child| child.kind() == "catch_type")) + .and_then(|ty| self.java_field_type(&active, ty, node, 0)); + let body = scope.child_by_field_name("body").unwrap_or(scope); + self.java_field_local( + scope, + name, + body.start_byte(), + body.end_byte(), + nominal, + )?; + } + } + "enhanced_for_statement" | "resource" => { + if let Some(name) = node.child_by_field_name("name") { + let scope = if node.kind() == "resource" { + ancestors(node) + .find(|ancestor| ancestor.kind() == "try_with_resources_statement") + } else { + Some(node) + }; + if let Some(scope) = scope + && let Some(body) = scope.child_by_field_name("body") + { + let nominal = node + .child_by_field_name("type") + .and_then(|ty| self.java_field_type(&active, ty, node, 0)); + let start = if node.kind() == "resource" { + name.start_byte() + } else { + body.start_byte() + }; + self.java_field_local(scope, name, start, body.end_byte(), nominal)?; + } + } + } + "lambda_expression" => { + if let (Some(parameters), Some(body)) = ( + node.child_by_field_name("parameters"), + node.child_by_field_name("body"), + ) { + if parameters.kind() == "identifier" { + self.java_field_local( + node, + parameters, + body.start_byte(), + body.end_byte(), + None, + )?; + } else if parameters.kind() == "inferred_parameters" { + let mut cursor = parameters.walk(); + for name in parameters + .named_children(&mut cursor) + .filter(|n| n.kind() == "identifier") + { + self.java_field_local( + node, + name, + body.start_byte(), + body.end_byte(), + None, + )?; + } + } + } + } + "instanceof_expression" => self.index_java_field_pattern(node, &active)?, + kind if kind.ends_with("_pattern") => { + let mut names = Vec::new(); + collect_nodes(node, "identifier", &mut names); + for name in names { + self.java_field_unknown_pattern(node, name)?; + } + } + _ => {} + } + let mut cursor = node.walk(); + for child in node.named_children(&mut cursor) { + self.index_java_field_values(child, &active)?; + } + Ok(()) + } + + fn java_field_local( + &mut self, + scope: Node<'_>, + name: Node<'_>, + start: usize, + end: usize, + nominal: Option, + ) -> Result<(), EvidenceError> { + ensure_capacity( + "Java field lexical bindings", + self.java_fields.bindings, + self.builder.limits.bindings, + )?; + self.java_fields.bindings += 1; + let spelling = self.text(name); + let bindings = self + .java_fields + .locals + .entry(scope.id()) + .or_default() + .entry(spelling) + .or_default(); + // Valid ordinary Java scopes have one binding per name. A crowded + // malformed/flow-sensitive scope becomes one unknown shadow barrier, + // so lookup never scans an unbounded version list or selects a winner. + if bindings.len() >= 2 { + bindings.clear(); + bindings.push(Local { + start: 0, + end: usize::MAX, + nominal: None, + }); + } + bindings.push(Local { + start, + end, + nominal, + }); + Ok(()) + } + + fn java_field_type( + &self, + owner: &DeclarationContext, + ty: Node<'_>, + declarator: Node<'_>, + depth: usize, + ) -> Option { + if depth >= RECEIVER_DEPTH || self.overlaps_parser_error(ty) { + return None; + } + let text = self.text(ty); + if text.contains('|') { + return None; + } + let mut raw = java_normalize_type(&text); + if raw.is_empty() || raw == "var" || raw.contains('|') { + return None; + } + if let Some(dimensions) = declarator.child_by_field_name("dimensions") { + raw.push_str(&java_dimensions_suffix(dimensions)); + } + if declarator.kind() == "spread_parameter" { + raw.push_str("[]"); + } + let base = raw.trim_end_matches("[]"); + let suffix = &raw[base.len()..]; + if java_primitive_type(base) { + return None; + } + // Type parameters shadow same-named classes. A single explicit bound + // can prove field ownership; intersections and recursive bounds cannot. + for scope in ancestors(ty) { + if let Some(parameters) = scope.child_by_field_name("type_parameters") { + let mut cursor = parameters.walk(); + let parameters = parameters + .named_children(&mut cursor) + .take(DEPTH + 1) + .collect::>(); + if parameters.len() > DEPTH { + return None; + } + for parameter in parameters { + if first_named(parameter).is_some_and(|name| self.text(name) == base) { + let mut cursor = parameter.walk(); + let bound = parameter + .named_children(&mut cursor) + .find(|n| n.kind() == "type_bound")?; + let mut cursor = bound.walk(); + let mut bounds = + bound.named_children(&mut cursor).filter(|n| !n.is_extra()); + let first = bounds.next()?; + if bounds.next().is_some() { + return None; + } + return self + .java_field_type(owner, first, first, depth + 1) + .map(|target| format!("{target}{suffix}")); + } + } + } + } + let head = base.split('.').next()?; + if ancestors(ty) + .last() + .is_some_and(|node| node.parent().is_some()) + || self.java_field_local_type(ty, head) + { + return None; + } + if self.visible_import_binding_is_ambiguous(owner, head) { + return None; + } + self.java_qualified_type(owner, base, ty.start_byte()) + .map(|target| format!("{target}{suffix}")) + } + + fn java_field_local_type(&self, node: Node<'_>, name: &str) -> bool { + ancestors(node).any(|scope| { + self.java_fields + .local_types + .get(&scope.id()) + .and_then(|names| names.get(name)) + .is_some_and(|start| *start <= node.start_byte()) + }) + } + + fn java_field_value(&self, node: Node<'_>, name: &str) -> Value<'_> { + let mut instance = true; + for scope in ancestors(node) { + if let Some(bindings) = self + .java_fields + .locals + .get(&scope.id()) + .and_then(|names| names.get(name)) + { + let mut matching = bindings.iter().filter(|binding| { + binding.start <= node.start_byte() && node.start_byte() < binding.end + }); + if let Some(binding) = matching.next() { + return if matching.next().is_some() { + Value::Unknown + } else { + Value::Local(binding.nominal.as_deref()) + }; + } + } + if java_container_kind(scope.kind()).is_none() + && self.java_fields.static_scopes.contains(&scope.id()) + { + instance = false; + } + if anonymous_body(scope) { + return Value::Unknown; + } + if java_container_kind(scope.kind()).is_some() { + let Some(context) = self.java_containers.get(&scope.id()) else { + return Value::Unknown; + }; + if let Some(fields) = self + .java_fields + .fields + .get(&context.qualified_name) + .and_then(|fields| fields.get(name)) + { + return match fields.as_slice() { + [field] if instance || field.is_static => Value::Field(field), + _ => Value::Unknown, + }; + } + if self.java_fields.static_scopes.contains(&scope.id()) { + instance = false; + } + // An inherited member can hide an enclosing field. Cross-file + // hierarchy selection belongs to the resolver, never this map. + if scope.child_by_field_name("superclass").is_some() + || scope.child_by_field_name("interfaces").is_some() + || scope.kind() == "enum_declaration" + || scope.kind() == "record_declaration" + { + return Value::Unknown; + } + } + } + if ancestors(node) + .last() + .is_some_and(|node| node.parent().is_none()) + { + Value::Absent + } else { + Value::Unknown + } + } + + fn java_field_this(&self, node: Node<'_>, named: Option<&str>) -> Option { + for scope in ancestors(node) { + if java_container_kind(scope.kind()).is_none() + && self.java_fields.static_scopes.contains(&scope.id()) + { + return None; + } + if named.is_none() && anonymous_body(scope) { + return None; + } + if java_container_kind(scope.kind()).is_some() { + let context = self.java_containers.get(&scope.id())?; + if named.is_none_or(|name| context.name == name) { + return Some(context.qualified_name.clone()); + } + if self.java_fields.static_scopes.contains(&scope.id()) { + return None; + } + } + } + None + } + + fn java_field_receiver( + &self, + owner: &DeclarationContext, + node: Node<'_>, + depth: usize, + ) -> Option { + if depth >= RECEIVER_DEPTH || self.overlaps_parser_error(node) { + return None; + } + match node.kind() { + "this" => self.java_field_this(node, None), + "identifier" => match self.java_field_value(node, &self.text(node)) { + Value::Local(target) => target.map(str::to_owned), + Value::Field(field) => field.nominal.clone(), + Value::Unknown => None, + Value::Absent => { + let name = self.text(node); + if self.java_field_local_type(node, &name) + || self.visible_import_binding_is_ambiguous(owner, &name) + { + return None; + } + self.local_target_for(owner, &name).cloned().or_else(|| { + self.imported_target_for_occurrence(owner, &name, node.start_byte(), true) + .cloned() + }) + } + }, + "parenthesized_expression" => { + self.java_field_receiver(owner, first_named(node)?, depth + 1) + } + "cast_expression" => { + self.java_field_type(owner, node.child_by_field_name("type")?, node, 0) + } + "array_access" => self + .java_field_receiver(owner, node.child_by_field_name("array")?, depth + 1)? + .strip_suffix("[]") + .map(str::to_owned), + "object_creation_expression" => { + let mut cursor = node.walk(); + if node + .named_children(&mut cursor) + .any(|n| n.kind() == "class_body") + { + return None; + } + let mut cursor = node.walk(); + if node + .children(&mut cursor) + .find(|n| !n.is_extra()) + .is_none_or(|n| n.kind() != "new") + { + return None; + } + self.java_field_type(owner, node.child_by_field_name("type")?, node, 0) + } + "field_access" => { + let object = node.child_by_field_name("object")?; + let field = node.child_by_field_name("field")?; + if field.kind() == "this" { + return self.java_field_this(node, Some(&self.text(object))); + } + let receiver = self.java_field_receiver(owner, object, depth + 1)?; + let fields = self + .java_fields + .fields + .get(&receiver)? + .get(&self.text(field))?; + match fields.as_slice() { + [field] => field.nominal.clone(), + _ => None, + } + } + _ => None, + } + } + + pub(super) fn add_java_field_access( + &mut self, + node: Node<'_>, + owner: &DeclarationContext, + ) -> Result<(), EvidenceError> { + if self.overlaps_parser_error(node) { + return Ok(()); + } + let owner_known = ancestors(node) + .find(|scope| { + matches!( + scope.kind(), + "method_declaration" | "constructor_declaration" + ) || java_container_kind(scope.kind()).is_some() + || anonymous_body(*scope) + }) + .is_some_and(|scope| { + if java_container_kind(scope.kind()).is_some() { + self.java_containers + .get(&scope.id()) + .is_some_and(|context| { + owner.enclosing_type_qualified_name.as_deref() + == Some(context.qualified_name.as_str()) + }) + } else { + self.declarations + .get(&scope.id()) + .is_some_and(|context| context.fact_id == owner.fact_id) + } + }); + let (field, qualifier, qualified_name) = if node.kind() == "field_access" { + let (Some(object), Some(field)) = ( + node.child_by_field_name("object"), + node.child_by_field_name("field"), + ) else { + return Ok(()); + }; + if field.kind() != "identifier" { + return Ok(()); + } + let target = owner_known + .then(|| self.java_field_receiver(owner, object, 0)) + .flatten() + .filter(|target| !target.ends_with("[]")) + .map(|target| format!("{target}::{}", self.text(field))); + (field, self.text(object), target) + } else { + if !owner_known || !java_value_identifier(node) { + return Ok(()); + } + let Value::Field(field) = self.java_field_value(node, &self.text(node)) else { + return Ok(()); + }; + let qualifier = if field.is_static { + field + .qualified + .rsplit_once("::") + .map(|(owner, _)| owner) + .unwrap_or_default() + .to_owned() + } else { + "this".to_owned() + }; + (node, qualifier, Some(field.qualified.clone())) + }; + let spelling = self.text(field); + let occurrence = self.builder.occur_with_context( + SemanticRole::MemberAccess, + &owner.fact_id, + &spelling, + Some(&qualifier), + Some(&owner.scope_id), + Some("member"), + range_for_node(self.source_file, field), + )?; + self.builder.relate( + CandidateRelation::AccessesMember, + &owner.fact_id, + Some(&occurrence), + None, + &spelling, + ResolutionConstraint { + exact_language: Some(self.language.to_owned()), + module_or_package: Some(self.module_or_package.clone()), + scope_id: Some(owner.scope_id.clone()), + qualified_name, + allowed_target_kinds: vec!["field".to_owned()], + allow_external: false, + ..ResolutionConstraint::default() + }, + )?; + Ok(()) + } + + fn index_java_field_pattern( + &mut self, + node: Node<'_>, + owner: &DeclarationContext, + ) -> Result<(), EvidenceError> { + let Some(name) = node.child_by_field_name("name") else { + return Ok(()); + }; + let mut condition = node; + let mut negative = false; + for _ in 0..DEPTH { + let Some(parent) = condition.parent() else { + break; + }; + if parent.kind() == "parenthesized_expression" { + condition = parent; + } else if parent.kind() == "unary_expression" + && self.text(parent).trim_start().starts_with('!') + { + negative = !negative; + condition = parent; + } else { + break; + } + } + if let Some(statement) = condition + .parent() + .filter(|parent| parent.kind() == "if_statement") + && statement + .child_by_field_name("condition") + .is_some_and(|n| n.id() == condition.id()) + { + let nominal = node + .child_by_field_name("right") + .and_then(|ty| self.java_field_type(owner, ty, ty, 0)); + let selected = if negative { + "alternative" + } else { + "consequence" + }; + if let Some(branch) = statement.child_by_field_name(selected) { + self.java_field_local( + branch, + name, + branch.start_byte(), + branch.end_byte(), + nominal.clone(), + )?; + } + // A simple abrupt negative guard proves the pattern on the + // remainder of its enclosing block. Complex flow stays unknown. + let rejected = if negative { + "consequence" + } else { + "alternative" + }; + if let Some(rejected) = statement.child_by_field_name(rejected) + && let Some(block) = statement.parent().filter(|n| n.kind() == "block") + { + let after_type = java_abrupt_statement(rejected).then_some(nominal).flatten(); + self.java_field_local( + block, + name, + statement.end_byte(), + block.end_byte(), + after_type, + )?; + } + return Ok(()); + } + // Unsupported flow must not turn a pattern-bound value into an outer + // field. Conservatively mask the name for its entire callable. + self.java_field_unknown_pattern(node, name) + } + + fn java_field_unknown_pattern( + &mut self, + node: Node<'_>, + name: Node<'_>, + ) -> Result<(), EvidenceError> { + let scope = ancestors(node) + .find(|n| { + matches!( + n.kind(), + "method_declaration" | "constructor_declaration" | "lambda_expression" + ) + }) + .or_else(|| ancestors(node).find(|n| java_container_kind(n.kind()).is_some())); + if let Some(scope) = scope { + self.java_field_local(scope, name, scope.start_byte(), scope.end_byte(), None)?; + } + Ok(()) + } +} + +fn java_abrupt_statement(node: Node<'_>) -> bool { + if matches!(node.kind(), "return_statement" | "throw_statement") { + return true; + } + if node.kind() != "block" { + return false; + } + let mut cursor = node.walk(); + let mut statements = node.named_children(&mut cursor).filter(|n| !n.is_extra()); + let first = statements.next(); + statements.next().is_none() + && first.is_some_and(|n| matches!(n.kind(), "return_statement" | "throw_statement")) +} + +fn java_value_identifier(node: Node<'_>) -> bool { + let Some(parent) = node.parent() else { + return false; + }; + let in_field = |name| { + parent + .child_by_field_name(name) + .is_some_and(|n| n.id() == node.id()) + }; + match parent.kind() { + "method_invocation" | "field_access" => in_field("object"), + "variable_declarator" | "resource" => in_field("value"), + "lambda_expression" => in_field("body"), + "enhanced_for_statement" => in_field("value"), + "cast_expression" => in_field("value"), + "instanceof_expression" => in_field("left"), + "argument_list" + | "return_statement" + | "throw_statement" + | "yield_statement" + | "expression_statement" + | "binary_expression" + | "unary_expression" + | "update_expression" + | "assignment_expression" + | "parenthesized_expression" + | "ternary_expression" + | "array_access" + | "array_initializer" + | "assert_statement" => true, + _ => false, + } +} diff --git a/crates/compass-languages/tests/java_field_access.rs b/crates/compass-languages/tests/java_field_access.rs new file mode 100644 index 000000000..c08dfc800 --- /dev/null +++ b/crates/compass-languages/tests/java_field_access.rs @@ -0,0 +1,224 @@ +use std::error::Error; +use std::path::Path; + +use compass_languages::{ + CandidateRelation, Engine, EvidenceLimits, SemanticRole, validate_evidence, +}; + +type Target = (String, Option); +fn targets(source: &str) -> Result, Box> { + let evidence = Engine::default() + .extract_source(Path::new("Box.java"), source.as_bytes())? + .semantic_evidence + .ok_or("missing Java evidence")?; + validate_evidence(&evidence, EvidenceLimits::default())?; + let mut result = Vec::new(); + for candidate in &evidence.candidates { + if candidate.relation != CandidateRelation::AccessesMember { + continue; + } + assert_eq!(candidate.constraints.allowed_target_kinds, ["field"]); + assert!(!candidate.constraints.allow_external); + assert!(candidate.binding_id.is_none()); + let occurrence = evidence + .occurrences + .iter() + .find(|o| Some(&o.id) == candidate.occurrence_id.as_ref()) + .ok_or("missing occurrence")?; + assert_eq!(occurrence.role, SemanticRole::MemberAccess); + assert!(occurrence.qualifier.is_some()); + let start = usize::try_from(occurrence.range.start_byte)?; + let end = usize::try_from(occurrence.range.end_byte)?; + assert_eq!( + source.get(start..end), + Some(candidate.target_spelling.as_str()) + ); + result.push(( + candidate.target_spelling.clone(), + candidate.constraints.qualified_name.clone(), + )); + } + result.sort(); + Ok(result) +} +fn field(name: &str, owner: &str) -> Target { + (name.into(), Some(format!("p.{owner}::{name}"))) +} + +#[test] +fn own_fields_preserve_occurrences_and_parameter_shadowing() -> Result<(), Box> { + let source = "package p; class Box { int value; Box(int value) { this.value = value; } int get() { return value + this.value; } int argument(int value) { return value; } }"; + assert_eq!(targets(source)?, vec![field("value", "Box"); 3]); + Ok(()) +} +#[test] +fn local_scope_exit_restores_field_binding() -> Result<(), Box> { + let source = "package p; class Box { int value; void run() { sink(value); { int value = 1; sink(value); } sink(value); for (int value = 0; value < 1; value++) { sink(value); } sink(value); } void sink(int x) {} }"; + assert_eq!(targets(source)?, vec![field("value", "Box"); 3]); + Ok(()) +} +#[test] +fn lexical_receivers_do_not_use_shadowed_or_out_of_scope_types() -> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { Cell item; void run(Cell param) { sink(param.value); sink(item.value); { var item = unknown(); sink(item.value); } sink(item.value); } }"; + let mut expected = vec![ + field("item", "Box"), + field("item", "Box"), + field("value", "Cell"), + field("value", "Cell"), + field("value", "Cell"), + ("value".into(), None), + ]; + expected.sort(); + assert_eq!(targets(source)?, expected); + Ok(()) +} +#[test] +fn lambdas_keep_this_but_parameters_catch_and_resources_shadow_fields() -> Result<(), Box> +{ + let source = "package p; class Box { int value; Object failure; Token resource; void run() { F f = value -> value; G g = () -> this.value; try {} catch (Exception failure) { sink(failure); } sink(failure); try (Token resource = new Token()) { sink(resource.value); } catch (Exception e) { sink(resource.value); } } } class Token { int value; }"; + let mut expected = vec![ + field("value", "Box"), + field("failure", "Box"), + field("resource", "Box"), + field("value", "Token"), + field("value", "Token"), + ]; + expected.sort(); + assert_eq!(targets(source)?, expected); + Ok(()) +} +#[test] +fn declaration_type_label_and_method_names_are_not_field_occurrences() -> Result<(), Box> +{ + let source = "package p; class Box { int value; int value() { return 1; } void run() { value(); value: while (true) { break value; } } }"; + assert!(targets(source)?.is_empty()); + Ok(()) +} +#[test] +fn nested_this_and_unknown_class_boundaries_are_distinct() -> Result<(), Box> { + let source = "package p; class Box { int value; class Inner { int value; int run() { return value + Box.this.value; } } void run() { class Local { int value; int get() { return this.value; } } F f = new F() { int value; int get() { return this.value; } }; } }"; + let mut expected = vec![ + field("value", "Box"), + field("value", "Box::Inner"), + ("value".into(), None), + ("value".into(), None), + ]; + expected.sort(); + assert_eq!(targets(source)?, expected); + Ok(()) +} +#[test] +fn typed_cast_array_and_generic_bound_receivers_retain_nominal_fields() -> Result<(), Box> +{ + let source = "package p; class Cell { int value; } class Box { Cell[] items; int run(Object unknown, Cell item) { return items[0].value + ((Cell) unknown).value + item.value; } int generic(T item) { return item.value; } }"; + let mut expected = vec![field("items", "Box")]; + expected.extend(vec![field("value", "Cell"); 4]); + expected.sort(); + assert_eq!(targets(source)?, expected); + Ok(()) +} +#[test] +fn ambiguous_imports_and_unknown_receivers_do_not_invent_field_targets() +-> Result<(), Box> { + for source in [ + "package p; import a.Cell; import b.Cell; class Box { int run(Cell item) { return item.value; } }", + "package p; class Box { int run() { return unknown().value; } }", + ] { + assert_eq!(targets(source)?, vec![("value".into(), None)], "{source}"); + } + Ok(()) +} + +#[test] +fn simple_pattern_branches_and_abrupt_guards_keep_flow_scope() -> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { Cell item; int positive(Object obj) { if (obj instanceof Cell item) { return item.value; } return item.value; } int negative(Object obj) { if (!(obj instanceof Cell item)) return 0; return item.value; } }"; + let mut expected = vec![field("item", "Box")]; + expected.extend(vec![field("value", "Cell"); 3]); + expected.sort(); + assert_eq!(targets(source)?, expected); + Ok(()) +} +#[test] +fn complex_pattern_flow_blocks_outer_field_fallback() -> Result<(), Box> { + for source in [ + "package p; class Cell { int value; } class Box { Cell item; int run(Object obj) { if (!(obj instanceof Cell item)) { log(); return 0; } return item.value; } }", + "package p; class Cell { int value; } class Box { Cell item; int run(Object obj) { if (obj instanceof Cell item && item.value > 0) return 1; return 0; } }", + ] { + assert_eq!(targets(source)?, vec![("value".into(), None)]); + } + Ok(()) +} +#[test] +fn static_nested_instances_are_distinct_from_static_callable_contexts() -> Result<(), Box> +{ + let source = "package p; class Box { int value; static class Inner { int value; int own() { return this.value + value; } static int invalid() { return value; } } }"; + assert_eq!(targets(source)?, vec![field("value", "Box::Inner"); 2]); + Ok(()) +} +#[test] +fn unregistered_local_callable_does_not_attribute_typed_access_to_outer_method() +-> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { void run() { class Local { int get(Cell item) { return item.value; } } } }"; + assert_eq!(targets(source)?, vec![("value".into(), None)]); + Ok(()) +} +#[test] +fn inherited_members_never_fall_back_to_an_enclosing_field() -> Result<(), Box> { + let source = "package p; class Box { int value; class Inner extends Unknown { int get() { return value; } } }"; + assert!(targets(source)?.is_empty()); + Ok(()) +} +#[test] +fn receiver_depth_exhaustion_is_unresolved() -> Result<(), Box> { + let source = format!( + "package p; class Cell {{ int value; }} class Box {{ int get(Cell item) {{ return {}item{}.value; }} }}", + "(".repeat(30), + ")".repeat(30) + ); + assert_eq!(targets(&source)?, vec![("value".into(), None)]); + Ok(()) +} + +#[test] +fn local_type_declaration_shadows_a_same_named_top_level_type() -> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { int run() { class Cell { int value; } Cell item = new Cell(); return item.value; } }"; + assert_eq!(targets(source)?, vec![("value".into(), None)]); + Ok(()) +} +#[test] +fn multi_catch_and_intersection_bounds_remain_unknown() -> Result<(), Box> { + for source in [ + "package p; class Box { int run() { try {} catch (First | Second item) { return item.value; } } }", + "package p; class Box { int run(T item) { return item.value; } }", + ] { + assert_eq!(targets(source)?, vec![("value".into(), None)]); + } + Ok(()) +} + +#[test] +fn enhanced_loop_binding_starts_after_the_iterable_expression() -> Result<(), Box> { + let source = "package p; // λ\nclass Cell { int value; } class Box { Cell[] items; void run() { for (Cell items : items) { sink(items.value); } } }"; + assert_eq!( + targets(source)?, + vec![field("items", "Box"), field("value", "Cell")] + ); + Ok(()) +} +#[test] +fn declared_field_types_keep_their_declaration_scope() -> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { Cell item; class Inner { class Cell { int value; } int run() { return item.value; } } }"; + assert_eq!( + targets(source)?, + vec![field("item", "Box"), field("value", "Cell")] + ); + Ok(()) +} + +#[test] +fn unregistered_class_initializer_never_uses_enclosing_callable_ownership() +-> Result<(), Box> { + let source = "package p; class Cell { int value; } class Box { void run() { class Local { int value = new Cell().value; } } }"; + assert_eq!(targets(source)?, vec![("value".into(), None)]); + Ok(()) +} diff --git a/crates/compass-resolve/tests/java_field_access.rs b/crates/compass-resolve/tests/java_field_access.rs new file mode 100644 index 000000000..11c84ddb3 --- /dev/null +++ b/crates/compass-resolve/tests/java_field_access.rs @@ -0,0 +1,151 @@ +use std::collections::{BTreeSet, HashMap}; +use std::error::Error; +use std::path::Path; + +use compass_graph::{BuildEvidence, normalize_v1}; +use compass_languages::{Engine, Extraction}; +use compass_model::code_graph::{EdgeKind, NodeKind}; +use compass_resolve::resolve; + +fn extract(file: &str, source: &str) -> Result> { + Ok(Engine::default().extract_source(Path::new(file), source.as_bytes())?) +} + +#[test] +fn cross_file_fields_publish_exact_caller_direction_occurrences_and_stable_order() +-> Result<(), Box> { + let model = "package model; public class Cell { public int value; }\n"; + let caller = "package app;\nimport model.Cell;\nclass Box {\n void run(Cell cell) {\n cell.value = cell.value + 1;\n }\n}\n"; + let directory = tempfile::tempdir()?; + std::fs::create_dir_all(directory.path().join("model"))?; + std::fs::create_dir_all(directory.path().join("app"))?; + std::fs::write(directory.path().join("model/Cell.java"), model)?; + std::fs::write(directory.path().join("app/Box.java"), caller)?; + let sources = HashMap::from([ + ("model/Cell.java".into(), model.into()), + ("app/Box.java".into(), caller.into()), + ]); + let inputs = vec![ + extract("model/Cell.java", model)?, + extract("app/Box.java", caller)?, + ]; + let resolved = resolve(&inputs, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let build = + BuildEvidence::from_extraction(directory.path(), &resolved, "sha256:java-field-access")?; + let graph = normalize_v1(resolved, build)?; + let run = graph + .nodes + .iter() + .find(|n| n.qualified_name == "app.Box::run") + .ok_or("missing run")?; + let field = graph + .nodes + .iter() + .find(|n| n.name == "value" && n.kind == NodeKind::Field) + .ok_or("missing field")?; + let edges = graph + .links + .iter() + .filter(|e| e.source == run.id && e.target == field.id) + .collect::>(); + assert_eq!(edges.len(), 2, "{:#?}", graph.links); + let mut starts = BTreeSet::new(); + for edge in edges { + assert_eq!(edge.kind, EdgeKind::References); + assert!( + edge.occurrence_rule + .as_ref() + .is_some_and(|rule| rule.as_str().starts_with("universal-member-access-")) + ); + let site = edge + .relationship_site + .as_ref() + .ok_or("missing occurrence")?; + assert_eq!(site.file, "app/Box.java"); + assert_eq!(site.start_line, 5); + let start = usize::try_from(site.start_byte)?; + let end = usize::try_from(site.end_byte)?; + assert_eq!(caller.get(start..end), Some("value")); + assert!(starts.insert(start)); + assert!(edge.evidence.iter().any(|e| e.anchors.contains(site))); + } + let mut reversed = inputs; + reversed.reverse(); + let second = resolve(&reversed, &sources); + let build = + BuildEvidence::from_extraction(directory.path(), &second, "sha256:java-field-access")?; + let second = normalize_v1(second, build)?; + assert_eq!( + serde_json::to_value(&graph.nodes)?, + serde_json::to_value(&second.nodes)? + ); + assert_eq!( + serde_json::to_value(&graph.links)?, + serde_json::to_value(&second.links)? + ); + Ok(()) +} + +#[test] +fn ambiguous_unknown_inherited_and_unregistered_owners_never_publish_false_field_edges() +-> Result<(), Box> { + for source in [ + "package p; class Cell { int value; } class Box { void run() { class Local { int value = new Cell().value; } } }", + "package p; class Cell { int value; int value; } class Box { int run(Cell item) { return item.value; } }", + "package p; class Cell { int value; } class Box { int run(Unknown item) { return item.value; } }", + "package p; class Cell { int value; } class Box { void run() { class Local { int get(Cell item) { return item.value; } } } }", + "package p; class Box { int value; class Inner extends Unknown { int get() { return value; } } }", + "package p; import a.*; class Cell { int value; } class Box { int run(Unknown item) { return item.value; } }", + "package p; class Cell { int value; } class Box { Cell item; int run(Object obj) { if (!(obj instanceof Cell item)) { log(); return 0; } return item.value; } }", + ] { + let resolved = resolve( + &[extract("Box.java", source)?], + &HashMap::from([("Box.java".into(), source.into())]), + ); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + assert!( + !resolved + .edges + .iter() + .any(|edge| edge.string("relation") == "accesses"), + "{source}: {:#?}", + resolved.edges + ); + } + Ok(()) +} + +#[test] +fn state_access_is_separate_from_method_selector_and_read_write_classification() +-> Result<(), Box> { + let source = "package p; class Cell { int value; int get() { return value; } } class Box { Cell item; void run() { item.get(); } }"; + let resolved = resolve( + &[extract("Box.java", source)?], + &HashMap::from([("Box.java".into(), source.into())]), + ); + let fields = resolved + .nodes + .iter() + .filter(|n| n.string("symbol_kind") == "field") + .map(|n| n.id.as_str()) + .collect::>(); + let accesses = resolved + .edges + .iter() + .filter(|edge| edge.string("relation") == "accesses") + .collect::>(); + assert_eq!(accesses.len(), 2); + assert!( + accesses + .iter() + .all(|edge| fields.contains(edge.target.as_str())) + ); + assert!( + resolved + .edges + .iter() + .any(|edge| edge.string("relation") == "calls") + ); + Ok(()) +} diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index 546e913b8..ab1d7b198 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -866,6 +866,27 @@ Do not infer support for another language or framework from file extensions, raw graph output, or total node and edge counts. +### Java field contacts + +Java field accesses use the existing `MemberAccess` / `AccessesMember` contract +and field-only qualified resolution. Exact field-token ranges retain repeated +uses. Unqualified names require a source-declared field after lexical value +lookup. The extraction index tracks block and loop exit, declaration order, +parameters, lambda/catch/resource bindings, and local type-name shadowing. +Declared nominal receivers, source-local field chains, arrays, casts, direct +construction and single generic bounds may establish a target. Simple pattern +branches and abrupt guards retain proven scope; unsupported pattern flow masks +possibly shadowed names. Scope traversal is capped at 64 ancestors and receiver +inference at 16 steps. Lexical records use the existing binding capacity; crowded +same-name scopes become unknown rather than requiring unbounded lookup. + +Graph v1 projects these contacts as `references` with member-access provenance. +They are not read/write effects, runtime identities or cohesion judgments. +Inherited fields, general access checking, cross-file field-type chains and +unregistered local/anonymous class ownership remain unsupported. Unknown +qualified receivers never select a same-named field. Schema and producer +capabilities are unchanged; AST cache semantics version 9 rebuilds prior facts. + ### Rust field contacts Rust field expressions now emit `MemberAccess` occurrences and `AccessesMember` From 5548a864e213b2355a4228647878f799114f186c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 05:54:56 -0700 Subject: [PATCH 85/97] audit: register Java known-ID field navigation control --- ...java_state_access_public_registration.json | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 benchmarks/agent_query/java_state_access_public_registration.json diff --git a/benchmarks/agent_query/java_state_access_public_registration.json b/benchmarks/agent_query/java_state_access_public_registration.json new file mode 100644 index 000000000..899e0ddcd --- /dev/null +++ b/benchmarks/agent_query/java_state_access_public_registration.json @@ -0,0 +1,65 @@ +{ + "schema": "compass.java-state-access-public-registration/1", + "registrationCommit": "09ca58e6a92ee6ec5045e7cbde14300d136b9e6a", + "scope": "Known-ID-assisted development retrieval control. Registered after implementation/native regressions, before rebuilt real-repository graphs or public responses are inspected. No identity-discovery, authored-answer, held-out or overall-superiority claim.", + "sourceRegistration": "benchmarks/agent_query/state_access_development_registration.json", + "sourceRegistrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "repository": "jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "tasks": [ + { + "owner": "Cleaner", + "state": "safelist", + "access": { + "method": "Cleaner", + "methodLine": 50, + "line": 52, + "column": 9, + "expression": "this.safelist", + "operation": "write", + "text": " this.safelist = safelist;" + } + }, + { + "owner": "Cleaner", + "state": "safelist", + "access": { + "method": "createSafeElement", + "methodLine": 188, + "line": 197, + "column": 17, + "expression": "safelist", + "operation": "read", + "text": " if (safelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) { // will keep this attr" + } + }, + { + "owner": "CleaningVisitor", + "state": "destination", + "access": { + "method": "CleaningVisitor", + "methodLine": 143, + "line": 145, + "column": 13, + "expression": "this.destination", + "operation": "write", + "text": " this.destination = destination;" + } + }, + { + "owner": "CleaningVisitor", + "state": "destination", + "access": { + "method": "head", + "methodLine": 148, + "line": 158, + "column": 21, + "expression": "destination", + "operation": "write", + "text": " destination = destChild;" + } + } + ], + "policy": "For each source-validated callable, provide each tool its own exact graph ID as task input and issue one public MCP get_neighbors call without relation filtering. Preserve native defaults, all errors, truncation and full response bytes; no retries or source follow-ups. Score field identity separately from selected-line anchors. Verify Compass full-node/parallel-record response against the graph; report payload costs without an efficiency claim. Reuse unchanged baseline Graphify graphs and the new Compass jsoup graph." +} From f2cacfebba15d69a738e7c14694d51320e4e07ba Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 06:17:29 -0700 Subject: [PATCH 86/97] Preserve Java field receiver type precedence and ambiguity --- COMPATIBILITY.md | 6 +- .../src/evidence/build/java_fields.rs | 25 ++- .../tests/java_field_access.rs | 24 +++ .../src/evidence/languages/java.rs | 44 +++++ .../src/evidence/languages/policy.rs | 4 +- .../tests/java_field_access.rs | 171 ++++++++++++++++++ docs/reference/universal-semantic-evidence.md | 5 +- 7 files changed, 274 insertions(+), 5 deletions(-) diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 64a940789..e300e9673 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -38,7 +38,11 @@ unqualified source-declared fields. A bounded AST lexical index distinguishes parameters, locals, block/loop lifetimes, lambda/catch/resource bindings and source type names. Receiver typing supports declared nominal values, source-local field chains, arrays, casts, direct constructors and single generic bounds. -Simple `instanceof` branches and abrupt guards retain their flow scope. +Simple `instanceof` branches and abrupt guards retain their flow scope. Visible +source types take precedence over imports and package prefixes. A qualified +field lookup requires one receiver type declaration; field availability cannot +select between duplicate nominal types. Exact declaration evidence remains +authoritative. Unknown receivers, ambiguous names, unsupported pattern flow, inherited fields, unregistered local/anonymous class owners and exhausted inference remain diff --git a/crates/compass-languages/src/evidence/build/java_fields.rs b/crates/compass-languages/src/evidence/build/java_fields.rs index 6d6586055..72bbea4fc 100644 --- a/crates/compass-languages/src/evidence/build/java_fields.rs +++ b/crates/compass-languages/src/evidence/build/java_fields.rs @@ -23,6 +23,7 @@ pub(super) struct JavaFieldIndex { locals: HashMap>>, local_types: HashMap>, fields: HashMap>>, + named_types: HashSet, bindings: usize, static_scopes: HashSet, } @@ -85,6 +86,13 @@ impl DirectEvidenceState<'_> { node: Node<'_>, owner: &DeclarationContext, ) -> Result<(), EvidenceError> { + if node.parent().is_none() { + self.java_fields.named_types = self + .java_containers + .values() + .map(|context| context.qualified_name.clone()) + .collect(); + } if is_static(node, self.source) { ensure_capacity( "Java field lexical bindings", @@ -331,6 +339,7 @@ impl DirectEvidenceState<'_> { } let base = raw.trim_end_matches("[]"); let suffix = &raw[base.len()..]; + let head = base.split('.').next()?; if java_primitive_type(base) { return None; } @@ -347,7 +356,10 @@ impl DirectEvidenceState<'_> { return None; } for parameter in parameters { - if first_named(parameter).is_some_and(|name| self.text(name) == base) { + if first_named(parameter).is_some_and(|name| self.text(name) == head) { + if head != base { + return None; + } let mut cursor = parameter.walk(); let bound = parameter .named_children(&mut cursor) @@ -366,7 +378,6 @@ impl DirectEvidenceState<'_> { } } } - let head = base.split('.').next()?; if ancestors(ty) .last() .is_some_and(|node| node.parent().is_some()) @@ -374,6 +385,16 @@ impl DirectEvidenceState<'_> { { return None; } + // A visible source type shadows imports and package prefixes. Once + // selected, a missing nested type cannot fall back to that package. + if let Some(local) = self.local_target_for(owner, head) { + let target = format!("{local}{}", base[head.len()..].replace('.', "::")); + return self + .java_fields + .named_types + .contains(&target) + .then(|| format!("{target}{suffix}")); + } if self.visible_import_binding_is_ambiguous(owner, head) { return None; } diff --git a/crates/compass-languages/tests/java_field_access.rs b/crates/compass-languages/tests/java_field_access.rs index c08dfc800..1d7e25d41 100644 --- a/crates/compass-languages/tests/java_field_access.rs +++ b/crates/compass-languages/tests/java_field_access.rs @@ -222,3 +222,27 @@ fn unregistered_class_initializer_never_uses_enclosing_callable_ownership() assert_eq!(targets(source)?, vec![("value".into(), None)]); Ok(()) } + +#[test] +fn member_type_shadows_same_named_import_for_field_receivers() -> Result<(), Box> { + let source = "package p; import remote.Cell; class Box { class Cell { int value; } int run(Cell item) { return item.value; } }"; + assert_eq!(targets(source)?, vec![field("value", "Box::Cell")]); + Ok(()) +} +#[test] +fn local_type_prefix_shadows_a_package_in_qualified_type_syntax() -> Result<(), Box> { + let source = "package p; class Box { class remote { class Cell { int value; } } int run(remote.Cell item) { return item.value; } }"; + assert_eq!(targets(source)?, vec![field("value", "Box::remote::Cell")]); + Ok(()) +} + +#[test] +fn shadowed_package_prefix_cannot_supply_missing_nested_types() -> Result<(), Box> { + for source in [ + "package p; class Box { class remote {} int run(remote.Cell item) { return item.value; } }", + "package p; class Box { int run(remote.Cell item) { return item.value; } }", + ] { + assert_eq!(targets(source)?, vec![("value".into(), None)]); + } + Ok(()) +} diff --git a/crates/compass-resolve/src/evidence/languages/java.rs b/crates/compass-resolve/src/evidence/languages/java.rs index 2502c8a8f..8cd68fd26 100644 --- a/crates/compass-resolve/src/evidence/languages/java.rs +++ b/crates/compass-resolve/src/evidence/languages/java.rs @@ -3,6 +3,50 @@ use super::super::*; impl ResolutionDb<'_> { + pub(in crate::evidence) fn resolve_java_field_receiver_ambiguity( + &self, + candidate: &RelationshipCandidate, + ) -> Option { + if candidate.relation != CandidateRelation::AccessesMember { + return None; + } + let (owner, _) = candidate + .constraints + .qualified_name + .as_deref()? + .rsplit_once("::")?; + let Some(declarations) = self + .indexes + .names + .by_qualified + .get(&("java".to_owned(), owner.to_owned())) + else { + return Some(ResolutionDecision::Unresolved); + }; + if declarations.len() > self.budget.candidates_per_lookup() { + return Some(ResolutionDecision::Ambiguous { + candidate_count: declarations.len(), + }); + } + // Member availability cannot choose among duplicate nominal types. + // Count receiver declarations before looking for the requested field. + let count = declarations + .iter() + .filter_map(|slot| self.declaration(*slot)) + .filter(|declaration| { + matches!( + declaration.kind.as_str(), + "class" | "interface" | "enum" | "record" | "annotation_type" + ) + }) + .count(); + match count { + 0 => Some(ResolutionDecision::Unresolved), + 1 => None, + candidate_count => Some(ResolutionDecision::Ambiguous { candidate_count }), + } + } + pub(in crate::evidence) fn resolve_java_same_package_builtin_collision( &self, candidate: &RelationshipCandidate, diff --git a/crates/compass-resolve/src/evidence/languages/policy.rs b/crates/compass-resolve/src/evidence/languages/policy.rs index 9973a5190..aa6c5afd1 100644 --- a/crates/compass-resolve/src/evidence/languages/policy.rs +++ b/crates/compass-resolve/src/evidence/languages/policy.rs @@ -66,7 +66,9 @@ impl LanguagePolicyKind { candidate, )) } - Self::Java => db.resolve_java_same_package_builtin_collision(candidate), + Self::Java => db + .resolve_java_field_receiver_ambiguity(candidate) + .or_else(|| db.resolve_java_same_package_builtin_collision(candidate)), Self::Kotlin => db.resolve_kotlin_candidate(candidate), Self::Ruby | Self::Swift | Self::Dart | Self::Scala | Self::Groovy | Self::Generic => { None diff --git a/crates/compass-resolve/tests/java_field_access.rs b/crates/compass-resolve/tests/java_field_access.rs index 11c84ddb3..4917702d4 100644 --- a/crates/compass-resolve/tests/java_field_access.rs +++ b/crates/compass-resolve/tests/java_field_access.rs @@ -149,3 +149,174 @@ fn state_access_is_separate_from_method_selector_and_read_write_classification() ); Ok(()) } + +#[test] +fn member_type_precedes_imported_type_across_files() -> Result<(), Box> { + let sources = HashMap::from([ + ("remote/Cell.java".into(), "package remote; public class Cell { public int value; }".into()), + ("p/Box.java".into(), "package p; import remote.Cell; class Box { class Cell { int value; } int run(Cell item) { return item.value; } }".into()), + ]); + let inputs = sources + .iter() + .map(|(file, source): (&String, &String)| extract(file, source)) + .collect::, _>>()?; + let resolved = resolve(&inputs, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + let targets = resolved + .edges + .iter() + .filter(|edge| edge.string("relation") == "accesses") + .map(|edge| { + resolved + .nodes + .iter() + .find(|node| node.id == edge.target) + .map(|node| node.string("qualified_name")) + }) + .collect::>(); + assert_eq!(targets, vec![Some("p.Box::Cell::value".to_owned())]); + Ok(()) +} + +#[test] +fn ambiguous_receiver_types_cannot_be_selected_by_field_availability() -> Result<(), Box> +{ + let sources = HashMap::from([ + ( + "one/Cell.java".into(), + "package p; class Cell { int value; }".into(), + ), + ( + "two/Cell.java".into(), + "package p; class Cell { int other; }".into(), + ), + ( + "p/Box.java".into(), + "package p; class Box { int run(Cell item) { return item.value; } }".into(), + ), + ]); + let inputs = sources + .iter() + .map(|(file, source): (&String, &String)| extract(file, source)) + .collect::, _>>()?; + for inputs in [inputs.clone(), inputs.into_iter().rev().collect()] { + let resolved = resolve(&inputs, &sources); + assert!(resolved.error.is_none(), "{:?}", resolved.error); + assert!( + !resolved + .edges + .iter() + .any(|edge| edge.string("relation") == "accesses"), + "{:#?}", + resolved.edges + ); + } + Ok(()) +} + +#[test] +fn field_receiver_decisions_preserve_ambiguity_limits_and_exact_evidence() +-> Result<(), Box> { + use compass_languages::{ + CandidateRelation, EvidenceBuilder, EvidenceLimits, EvidenceRange, ResolutionConstraint, + SemanticRole, UniversalEvidenceRegistry, + }; + use compass_resolve::evidence::{ + ResolutionDecision, UniversalResolutionIndex, UniversalResolutionLimits, + }; + let pipeline = UniversalEvidenceRegistry::pipeline("java").ok_or("Java pipeline")?; + for (receivers, budget, exact) in [ + (0, 256, false), + (1, 256, false), + (2, 256, false), + (2, 1, false), + (2, 256, true), + ] { + let range = |start: u32| EvidenceRange { + source_file: "fixture.java".into(), + start_byte: u64::from(start), + end_byte: u64::from(start + 1), + start_line: 1, + end_line: 1, + start_column: start, + end_column: start + 1, + }; + let mut builder = EvidenceBuilder::new( + pipeline, + "field-receiver-test", + "fixture.java", + EvidenceLimits::default(), + ); + let caller = builder.declare( + "method", + "caller", + "run", + "p.Box::run", + Some("p"), + None, + range(0), + )?; + let field = builder.declare( + "field", + "field", + "value", + "p.Cell::value", + Some("p"), + None, + range(2), + )?; + for i in 0..receivers { + builder.declare( + "class", + &format!("class-{i}"), + "Cell", + "p.Cell", + Some("p"), + None, + range(4 + i), + )?; + } + let occurrence = builder.occur( + SemanticRole::MemberAccess, + &caller, + "value", + Some("item"), + None, + range(10), + )?; + let candidate = builder.relate( + CandidateRelation::AccessesMember, + &caller, + Some(&occurrence), + None, + "value", + ResolutionConstraint { + qualified_name: Some("p.Cell::value".into()), + allowed_target_kinds: vec!["field".into()], + exact_target_declaration_id: exact.then(|| field.clone()), + ..ResolutionConstraint::default() + }, + )?; + let index = UniversalResolutionIndex::new( + &[builder.finish()?], + UniversalResolutionLimits { + candidates_per_lookup: budget, + ..UniversalResolutionLimits::default() + }, + )?; + let decision = index.resolve(&candidate); + if exact || receivers == 1 { + assert!( + matches!(decision, ResolutionDecision::Resolved { declaration_id, .. } if declaration_id == field) + ); + } else if receivers == 0 { + assert_eq!(decision, ResolutionDecision::Unresolved); + } else { + assert_eq!( + decision, + ResolutionDecision::Ambiguous { candidate_count: 2 } + ); + } + } + Ok(()) +} diff --git a/docs/reference/universal-semantic-evidence.md b/docs/reference/universal-semantic-evidence.md index ab1d7b198..dadb607f2 100644 --- a/docs/reference/universal-semantic-evidence.md +++ b/docs/reference/universal-semantic-evidence.md @@ -874,7 +874,10 @@ uses. Unqualified names require a source-declared field after lexical value lookup. The extraction index tracks block and loop exit, declaration order, parameters, lambda/catch/resource bindings, and local type-name shadowing. Declared nominal receivers, source-local field chains, arrays, casts, direct -construction and single generic bounds may establish a target. Simple pattern +construction and single generic bounds may establish a target. Visible source +types shadow imports and package prefixes, including nested type syntax. +Qualified Java field lookup stops on absent or ambiguous receiver types before +considering member availability; exact declaration evidence retains precedence. Simple pattern branches and abrupt guards retain proven scope; unsupported pattern flow masks possibly shadowed names. Scope traversal is capped at 64 ancestors and receiver inference at 16 steps. Lexical records use the existing binding capacity; crowded From d278c1e9f1e9c99e8a3ca07a7ebd4ceda40f5b63 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 06:32:22 -0700 Subject: [PATCH 87/97] Audit Java field contacts with compiler and paired repository evidence --- benchmarks/agent_query/README.md | 30 + .../java_state_access_development_review.json | 952 ++++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 108 ++ 3 files changed, 1090 insertions(+) create mode 100644 benchmarks/agent_query/java_state_access_development_review.json diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index b4e2e7cfb..e3bab2989 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -570,3 +570,33 @@ baseline into `--output benchmarks/agent_query/java_state_scope_baseline.json --verify`. Compilation/disassembly outputs, compiler identities, source, registration, class files and graph hashes are retained. Historical exploratory reports remain in the external artifact directory. + +### Java field-access correction + +`java_state_access_development_registration.json` retains all 20 real-source +sites and all 44 compiler cases; `java_state_access_public_registration.json` +registers four known-ID public neighbor requests. The corrected review is +`java_state_access_development_review.json`, with source, binary, graph, +registration and verification hashes. Complete captures and replay scripts are +under external `java-state-access-02`; `java-state-access-01` is explicitly +superseded after compiler and native counterexamples exposed wrong type +precedence and duplicate-receiver selection. + +Compass recovers 8/20 real-source sites (four Java and four Rust), versus +Graphify's unchanged 0/20. The Java fixture recovers 39/45 occurrences versus +0/45, retaining six misses and all eight negative controls. A separate +post-capture check verifies the supported compiler field/enclosing-method pairs +and source ranges; the original target/line inventory still does not score +caller ownership. This is a fixture-specific consistency check, not a general +compiler oracle or blinded precision result. + +All five repositories are rebuilt; four graphs are byte-identical to the Rust +baseline. jsoup retains previous records and adds 3,896 field references. All +added records pass endpoint/occurrence checks, including independent AST +ownership ranges for 26 field initializers. Four public requests retrieve the +selected field identities and anchors for Compass, versus none for Graphify; +Compass's complete response payload is substantially larger. The unchanged +75 task-pair community outcomes show no measured improvement. These results do +not establish authored explanations, cohesion, god-object defects, exhaustive +edge precision or overall superiority. See the audit document for remaining +misses, payload costs, partition changes, warnings and exact verification. diff --git a/benchmarks/agent_query/java_state_access_development_review.json b/benchmarks/agent_query/java_state_access_development_review.json new file mode 100644 index 000000000..9c4fc0d30 --- /dev/null +++ b/benchmarks/agent_query/java_state_access_development_review.json @@ -0,0 +1,952 @@ +{ + "schema": "compass.java-state-access-development-review/1", + "scope": "Known development sources and fixed access sites. Full graph inventory, compiler fixture and separate known-ID-assisted public retrieval control. Not held-out, representative god-object classification, exhaustive edge precision, authored explanations, read/write effects, or overall superiority.", + "productCommit": "f2cacfebba15d69a738e7c14694d51320e4e07ba", + "registrationCommits": [ + "5c10c9d4", + "5548a864" + ], + "registrationSha256": "fd2de77260e0fdfcbf5f6f47f30d96037c183a87491d3b5468b3ba309d16e773", + "sourceRegistrationSha256": "4805af71d9ae358fa65dba8394228d77a8203589f8f5201c20fe469c6aa3362d", + "scopeRegistrationSha256": "3a12b108736b5341ed09f0f9c66aa874e2612ef8d8e26afc542692fe0bdf445a", + "publicRegistrationSha256": "b20a8aabce091f8a3c57d25878cc97cdebd25bdfaa7e3a76074e75fc4476228a", + "artifactDirectory": "java-state-access-02", + "sourceSha256": { + "crates/compass-resolve/src/evidence/languages/java.rs": "949d444ed0eee178a2b11a46738ceb0fdad6ca0dbf52963960bc41b6f4b08861", + "crates/compass-resolve/src/evidence/languages/policy.rs": "d8dc456079c763d21c2a7cd8d3fc71d625f8087c24b87ca225321ac6d35fc2e0", + "crates/compass-languages/src/evidence/build/java_fields.rs": "22aeb543e345846fc72b22fca8bef3a1c5bd399d1048b5b6fabefb7a84dc73e9", + "crates/compass-languages/src/evidence/build.rs": "5c5efdcf5d3b738e9aa6b6382f59738c75cd8f21aa462c7e5a3b155197860898", + "crates/compass-files/src/cache.rs": "5c207692609e4c1da2285ec804eaedccce19229b9ee1a3971020ec15a1c40d77", + "crates/compass-files/tests/contracts.rs": "3a34cf1c6784d738bfede178a08e209d376c5b956a903a6614fd8db7e1a27fed", + "crates/compass-languages/tests/java_field_access.rs": "3196df595bd2af0a39e5b62db0f3e40c3210e60a911b530f3cf02acdcd519be9", + "crates/compass-resolve/tests/java_field_access.rs": "2658e695530f3b7b10d71094761370dbda9a82127a292edc3ff8565118c3effe" + }, + "compassBinarySha256": "5f6ff5f9b7b13866fdc7435e7a05888d3a95a0b23636be89cf45bc3478660139", + "graphResults": { + "compass": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 6, + "contactSupported": 8, + "selectedLineSupported": 8, + "status": { + "contact_edge": 8, + "missing_state": 8, + "no_contact_edge": 4 + } + }, + "graphify": { + "accessSites": 20, + "uniqueCallableSites": 20, + "uniqueStateSlots": 0, + "contactSupported": 0, + "selectedLineSupported": 0, + "status": { + "missing_state": 20 + } + } + }, + "graphDeltas": [ + { + "repository": "chi", + "oldGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "newGraphSha256": "988ee732b04750f609628858e7a3fce8f97fce7ba1af22e6c17c6c4d6561e7f5", + "nodesBefore": 729, + "nodesAfter": 729, + "edgesBefore": 1914, + "edgesAfter": 1914, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "click", + "oldGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "newGraphSha256": "311dbf6ea613493cd7d45a2dee7725c2a868ec58569cc6b236988fedf3afbc93", + "nodesBefore": 4264, + "nodesAfter": 4264, + "edgesBefore": 6387, + "edgesAfter": 6387, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "jsoup", + "oldGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "newGraphSha256": "2a9216926b1c5dfc82e4f88df0cce24a7a14ac67aa2a350158eff39018145314", + "nodesBefore": 6116, + "nodesAfter": 6116, + "edgesBefore": 21110, + "edgesAfter": 25006, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 5567, + "addedEdges": 3896, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "redux", + "oldGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "newGraphSha256": "8c40d1d66d19a90b395452505ce30d22a625759b3c5ff2c5b84223a68dd31b6b", + "nodesBefore": 3503, + "nodesAfter": 3503, + "edgesBefore": 5653, + "edgesAfter": 5653, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + }, + { + "repository": "walkdir", + "oldGraphSha256": "f7818d1c9aaf93b0b80d0f1a3a3fd2179b60ae17f9278185c9c3eb529f531034", + "newGraphSha256": "f7818d1c9aaf93b0b80d0f1a3a3fd2179b60ae17f9278185c9c3eb529f531034", + "nodesBefore": 288, + "nodesAfter": 288, + "edgesBefore": 1380, + "edgesAfter": 1380, + "addedNodes": 0, + "removedNodes": 0, + "changedNodes": 0, + "communityChanges": 0, + "addedEdges": 0, + "removedEdges": 0, + "changedEdges": 0 + } + ], + "scopeResults": { + "compass": { + "fieldDeclarations": 14, + "uniqueFields": 12, + "missingFields": 2, + "ambiguousFields": 0, + "fieldContactRecords": 39, + "registeredFieldOccurrences": 45, + "occurrenceTargetsMatched": 39, + "negativeCases": 8, + "negativeCasesWithContact": 0, + "callerOwnershipScored": false, + "edgePrecision": null + }, + "graphify": { + "fieldDeclarations": 14, + "uniqueFields": 0, + "missingFields": 14, + "ambiguousFields": 0, + "fieldContactRecords": 0, + "registeredFieldOccurrences": 45, + "occurrenceTargetsMatched": 0, + "negativeCases": 8, + "negativeCasesWithContact": 0, + "callerOwnershipScored": false, + "edgePrecision": null + } + }, + "scopeMissingOrUnexpected": { + "compass": [ + { + "id": "super_field", + "expected": [ + "base_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "static_fields", + "expected": [ + "scope_static", + "scope_static" + ], + "matched": 1, + "unexpected": [] + }, + { + "id": "inherited_beats_outer", + "expected": [ + "base_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "anonymous_this", + "expected": [ + "anonymous_value", + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "local_class_this", + "expected": [ + "local_value" + ], + "matched": 0, + "unexpected": [] + } + ], + "graphify": [ + { + "id": "constructor", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "explicit_this", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "implicit_this", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "repeated", + "expected": [ + "scope_value", + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "block_before", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "block_after", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "before_local", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "for_after", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "enhanced_iterable", + "expected": [ + "scope_items" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "enhanced_binding", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "enhanced_after", + "expected": [ + "scope_slot", + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "lambda_this", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "lambda_implicit", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "catch_after", + "expected": [ + "scope_failure" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "resource_binding", + "expected": [ + "token_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "resource_catch", + "expected": [ + "scope_resource", + "token_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "resource_after", + "expected": [ + "scope_resource", + "token_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "pattern_true", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "pattern_after", + "expected": [ + "scope_slot", + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "pattern_input", + "expected": [ + "scope_pattern" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "pattern_flow", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "typed_parameter", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "cast_receiver", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "field_chain", + "expected": [ + "scope_slot", + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "array_receiver", + "expected": [ + "scope_items", + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "super_field", + "expected": [ + "base_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "declared_receiver", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "hiding_receiver", + "expected": [ + "shadow_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "generic_bound", + "expected": [ + "cell_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "static_fields", + "expected": [ + "scope_static", + "scope_static" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "declarator_order", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "inner_own", + "expected": [ + "inner_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "qualified_this", + "expected": [ + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "inherited_beats_outer", + "expected": [ + "base_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "anonymous_this", + "expected": [ + "anonymous_value", + "scope_value" + ], + "matched": 0, + "unexpected": [] + }, + { + "id": "local_class_this", + "expected": [ + "local_value" + ], + "matched": 0, + "unexpected": [] + } + ] + }, + "scopeCallerConsistency": { + "schema": "compass.java-scope-caller-consistency/1", + "scope": "Post-capture compiler/graph consistency check on the registered synthetic fixture; not a newly blinded score. Original target/line inventory remains unchanged. Compiler lambda body names map to the enclosing source method, whose source extent must contain the access. All fixture callable names are unambiguous; no general overload identity adapter is claimed.", + "oracleSha256": "38fb8d38bccbc395cd06e5ec42fa7edcd7735f9ef4839e9a727d4a2af851a1e7", + "graphSha256": "9ba95283221028543ba431e6fb8ce6f3e0b33e46f7558aa71147488d2c94ad61", + "verifiedOccurrences": 39, + "missingOccurrences": 6 + }, + "addedOccurrenceConsistency": 3896, + "fieldInitializerOwnership": 26, + "publicResults": { + "compass": { + "tasks": 4, + "fieldNeighborIdentity": 4, + "selectedLineAnchored": 4, + "textBytes": 9883, + "wireBytes": 108999 + }, + "graphify": { + "tasks": 4, + "fieldNeighborIdentity": 0, + "selectedLineAnchored": 0, + "textBytes": 3313, + "wireBytes": 3708 + } + }, + "communityImpact": { + "scope": "Co-membership stability diagnostic, not responsibility quality or god-object classification.", + "beforeGraphSha256": "3e7c2ca56a3e4a69d27a809237e73d604f948161cc8ffb3e3e1316437a320ead", + "afterGraphSha256": "2a9216926b1c5dfc82e4f88df0cce24a7a14ac67aa2a350158eff39018145314", + "nodes": 6116, + "communitiesBefore": 41, + "communitiesAfter": 42, + "unassignedBefore": 0, + "unassignedAfter": 0, + "pairsBefore": 1110372, + "pairsAfter": 1077153, + "retained": 796307, + "separated": 314065, + "joined": 280846 + }, + "communityTasks": { + "before": { + "compass": { + "cross_task": { + "same_community": 18, + "different_community": 42, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 6, + "different_community": 38, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 13, + "different_community": 2, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 11, + "different_community": 2, + "unresolved": 0 + } + }, + "graphify": { + "cross_task": { + "same_community": 12, + "different_community": 48, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 44, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 12, + "different_community": 3, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 10, + "different_community": 3, + "unresolved": 0 + } + } + }, + "after": { + "compass": { + "cross_task": { + "same_community": 18, + "different_community": 42, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 6, + "different_community": 38, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 13, + "different_community": 2, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 11, + "different_community": 2, + "unresolved": 0 + } + }, + "graphify": { + "cross_task": { + "same_community": 12, + "different_community": 48, + "unresolved": 0 + }, + "cross_task/cross_file": { + "same_community": 0, + "different_community": 44, + "unresolved": 0 + }, + "cross_task/same_file": { + "same_community": 12, + "different_community": 4, + "unresolved": 0 + }, + "within_task": { + "same_community": 12, + "different_community": 3, + "unresolved": 0 + }, + "within_task/cross_file": { + "same_community": 2, + "different_community": 0, + "unresolved": 0 + }, + "within_task/same_file": { + "same_community": 10, + "different_community": 3, + "unresolved": 0 + } + } + }, + "changedPairs": [] + }, + "superseded": { + "directory": "java-state-access-01", + "reason": "Interim product 09ca58e6 selected an imported type over a member type; compiler proof and two failing native cases retained. Duplicate nominal receiver types also selected by field availability in a separate failing regression. Both fixed before final rerun. Interrupted qualification was not counted as passed." + }, + "checks": { + "preflight-clippy": { + "argv": [ + "cargo", + "clippy", + "-p", + "compass-languages", + "-p", + "compass-resolve", + "--test", + "java_field_access", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 0.31, + "reportSha256": "cdb84eaedadb049bd41ba65fc24565fc8ae8587b642abc9bb96432990f7f4895", + "logSha256": "9ae36bb22d8f0f810a77044760863f70b81405396a32cce477ec08eecd4e6d59" + }, + "fmt": { + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 3.51, + "reportSha256": "4d4bdcbd582b918f68b4371c94cda442736723f41f71a440c7d0e2bc8b339fbe", + "logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "build": { + "argv": [ + "cargo", + "build", + "-p", + "compass-cli", + "--bin", + "compass", + "--locked" + ], + "exitCode": 0, + "seconds": 40.96, + "reportSha256": "74dda0666605c13b90e3563e194ce541ee8a3304190c7d724d9a372a6f15e839", + "logSha256": "84b2cf417c28401956d22a63c60f5f78e4a03a88db1b4f3ba1499cd74ab1babf" + }, + "language-tests": { + "argv": [ + "cargo", + "test", + "-p", + "compass-languages", + "--test", + "java_field_access", + "--test", + "java_varargs", + "--test", + "java_universal_conformance", + "--locked" + ], + "exitCode": 0, + "seconds": 3.43, + "reportSha256": "ec2f2414ab76b5c12bddb03d4581af4d02267735e36163573ec3ebcbf59a7ba4", + "logSha256": "fc4f1e7ded1ca61f180e3dd7d90ae20fddd8d32e5aa9f7c6ba84fd0c2d549da9", + "testCounts": { + "passed": 27, + "failed": 0, + "ignored": 0 + } + }, + "resolver-tests": { + "argv": [ + "cargo", + "test", + "-p", + "compass-resolve", + "--test", + "java_field_access", + "--test", + "universal_evidence", + "--test", + "universal_resolution", + "--test", + "java_constructor_receivers", + "--test", + "java_varargs", + "--test", + "java_varargs_phases", + "--locked" + ], + "exitCode": 0, + "seconds": 6.78, + "reportSha256": "39e376e1cccd147887502478f8943a2ee35244efb71958eaf9cb95d714c4fc97", + "logSha256": "b8fdd62e9ebbae5540ace5e4ed95c947788629e393ab4b2b5ef895af6ddf0462", + "testCounts": { + "passed": 244, + "failed": 0, + "ignored": 0 + } + }, + "cache-tests": { + "argv": [ + "cargo", + "test", + "-p", + "compass-files", + "--test", + "contracts", + "--locked" + ], + "exitCode": 0, + "seconds": 1.19, + "reportSha256": "1cd1b1c3853fda02ea5cec11b44fb600bcb0c2de3036aaa6934f7d93e40238ac", + "logSha256": "e11e4f830f7e35a2fc7c7cace36ceaa556ab16c33226bbdf8f45e3cb5fa9c38a", + "testCounts": { + "passed": 33, + "failed": 0, + "ignored": 0 + } + }, + "clippy": { + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 19.87, + "reportSha256": "1994e48a3a5077f04b7dd885e324e9bd0a94e28a9e1f39cf6f3476ec49ab629b", + "logSha256": "6f15f3038220fcb1ed187df07944d683ff66e6ff78b8a36c0e776388ae9af8af" + }, + "focused-clippy": { + "argv": [ + "cargo", + "clippy", + "-p", + "compass-languages", + "-p", + "compass-resolve", + "--test", + "java_field_access", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 0.35, + "reportSha256": "ff991cb8170f78e713662fea13be0d76badf53bdcc7f1514fc84d26d0b0e27be", + "logSha256": "59e339d2aa27cd2222fead9549dbc67b0a6e0d10e368fb0ddf49b9c9ba0ac0a3" + }, + "workspace-tests": { + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 82.84, + "reportSha256": "be6d154bc770da4cdca83d5f28c5cd2688a6b85120eda17098cab160e815c6c9", + "logSha256": "36bfc8e761abce7e729a795b6236eaac6e076249eb3122c365754f1ba2ecd4fa", + "testCounts": { + "passed": 1106, + "failed": 0, + "ignored": 2 + } + }, + "product-tests": { + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 3.47, + "reportSha256": "a3880eca791cd111a56398188a7ff225774098c03c34c0991209af16de8132c7", + "logSha256": "27cc488ba8d06463219c6cb54a7766883fbc375c51dcae91e6a324a2db1584e5", + "testCounts": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "boundary": { + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.1, + "reportSha256": "babb0ce0501376cea10d60722855d84c56ba3169822708f8a522f9b07c0efcd1", + "logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "fixtures": { + "argv": [ + "bash", + "scripts/qualify_code_graph_v1.sh", + "--fixtures-only" + ], + "exitCode": 0, + "seconds": 666.34, + "reportSha256": "e156b76e25ef0cf0d571d5deff6354eb507bd694c42469638c51685fa3878bb3", + "logSha256": "dd6613ebd79db8ecd945a58f98b64b8c0806314eedb02b9619049446ca9262e9", + "testCounts": { + "passed": 6, + "failed": 0, + "ignored": 0 + } + }, + "benchmarks": { + "argv": [ + "python3", + "-m", + "unittest", + "discover", + "-s", + "benchmarks/agent_query/tests" + ], + "exitCode": 0, + "seconds": 2.04, + "reportSha256": "d06c8f8c4ec3eca2430ef69b18361dddf60c74720d17dbf824f1366b0560eb09", + "logSha256": "729939fb1dec4ad2b7023ded51a5676a1903ebe417b65092f82f8133af5021aa" + } + }, + "artifactSha256": { + "run.json": "c315bffe56cbb7a7684c088e0219fc85ba9ef0681a39463608845691522d21ff", + "review.json": "e9a01c941adc0c7d42a414ee714f8fc2378527c474f093a8b5095d74cc23d25f", + "verification.json": "dc7125509ffd7cf8032e63f7eddf8ec6611b27ca17c6a3044abb078e6debc746", + "scope-graphs.json": "b814930e4045cbe975765bdea277f8443cf26d435c4a6594560ac989c94eada8", + "scope-review.json": "a4447e67ce4fc99f781d37575ff8d8e75e9a89190820dcf90f7ecaa3b99d41b4", + "scope-caller-consistency.json": "8e2ff407ed288d9f26bc234f39e39dd27e372b4671aa7a0f1cd00fc45579b50a", + "public-capture.json": "7319b09d0b3c8fa01cddf4a36c1768975433623e31a5668bd73e84c421844bda", + "added-occurrence-consistency.json": "64287f4b09c871b98eae8bbda9d8f9c164cefac9952f3e3eb5a13c7c28b69a20", + "initializer-ownership.json": "42fff2ea76ff7dc1149acce39971f1897dfa2bef3eb1ba62d7e202387ae90b73", + "community-task-impact.json": "0ff221fee89d464db723f1ac8105f353b5ad9fa050a4052712ef014db80be011", + "community-impact.json": "db6c2f9d4fe1fa98da6568bfe6f47902c182c9491e710d6729690a2c897938b1", + "post-counterexample-delta.json": "d455a9d9c0e66d8dc4128f3925aae64492782c39c34d7f9f4bbda8b3434d936a", + "post-counterexample-source-review.json": "00e3aa8f2852bbe2227eb831ca037466d14fad2ead946213f006d35b7a37a5b4", + "precedence.json": "a8614a2c881b41b99afc3b90c422f98d4055da77b6e619467f82c30f5c7644f5", + "evaluate.py": "624a1483480829e1bada5b9858fdc47da64c616ca2e24d506be7fb72bf1bace6", + "scope.py": "c7fafb80c80f9a58baf6cbb2d3b494abc00851881c5a4acb242550cf41d06dfa", + "public.py": "da5a67dbea7ec84b5ff651c52d94d9f5c6afe2182c48b6e51210808dc6c17c39", + "verify.py": "061b40a069d788087bd1c06605565fcc4d21e312a66707ae6738cf8f72761454", + "verify-scope.py": "d39d18cc94102a47f65e4d5d57802c86c468b9322e78ac03a9c4609d51d77aa2", + "initializer-audit.py": "ff793fb6b8ffc93d80a6e6753556a430f8d4f1701398cacdd375431801813a00", + "initializer_ast.rs": "1d44c3f61bbb2a11766dbce28b4fef54d65a49dcc41196ab861accecc75cc32b", + "community-replay.py": "ebcab25f8453955ce80fa0a171d9b862a8a1b766562219c2d2b387c74b8fa165", + "community-impact.py": "bc62e80b990b2dbca98e8cfb0f458e17c118707e0b61e244ff11ab5ed3150620", + "precedence.py": "0b431b5174a11d82b700419b6b22e9d959c760e34feb3ba327393ef043ab6845", + "run.py": "e7e01fedde8cebd9b9994d59e58bbdaacc343d93a3076ffadb3349cacc12368b", + "validate-final.py": "623ca47c24b9e1882a3effde9093d5fb95d7b6e0c21592f824a8f2440c01841e" + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 82553236e..7564b0cb3 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -2954,6 +2954,114 @@ precedes compiler and graph observations. The graph inventory explicitly reports unscored caller ownership rather than treating a matching target/line as a full semantic-edge judgment. +## Java field contacts: correction and unchanged development comparisons + +Registration `5c10c9d4` fixes the same 20 real-repository access sites and all +44 compiler scope cases before production work. Public registration `5548a864` +fixes four known-ID neighbor requests after implementation but before rebuilt +real graphs or responses were inspected. These are development controls, not +held-out questions. Production commits `09ca58e6` and `f2cacfeb` add bounded Java +field evidence and correct two reproduced type-selection defects. + +The extractor now distinguishes lexical values from source-declared fields, +retains block/loop/lambda/catch/resource lifetimes, and records exact field-token +occurrences. Declared nominal receivers, arrays, casts, direct constructors, +source-local field chains and single generic bounds can establish an owner. +Simple flow patterns are supported; unsupported flow masks possibly shadowed +bindings. Unknown receivers, inherited fields, unregistered local/anonymous +owners and exhausted inference do not acquire convenient same-named targets. +The generic graph projection remains `references`, with member-access provenance; +it does not classify reads/writes or establish runtime alias identity. + +Two counterexamples invalidated the initial production capture. An imported +`remote.Cell` incorrectly beat a visible member `Box.Cell`; compiler bytecode +confirms the member type's field. Two files declaring the same receiver type +also allowed field availability to choose one declaration. Failing regressions +are retained. The correction gives visible source types precedence over imports +and package prefixes, and checks nominal receiver ambiguity before field lookup. +Parser-free decisions cover absent/unique/duplicate types, lookup limits and +exact-source precedence. The compiler counterexample now publishes the correct +`p.Box::Cell::value` target. `java-state-access-01` remains superseded evidence; +its interrupted fixture qualification is not counted as passed. + +### Fixed real-source and compiler results + +| Registered evidence | Compass before Java correction | Compass corrected | Graphify | +| --- | ---: | ---: | ---: | +| Five-repository field contacts with selected-line anchors | 4/20 | 8/20 | 0/20 | +| Compiler fixture occurrence targets | 0/45 | 39/45 | 0/45 | +| Compiler fixture field declarations | 12/14 | 12/14 | 0/14 | +| Negative compiler cases with field contacts | 0/8 | 0/8 | 0/8 | + +All four selected jsoup sites now connect the exact callable to `safelist` or +`destination` with selected-line anchors. The four Rust successes remain. +Go/Python lack eight registered state-site declarations; TypeScript has four +state sites without contacts. The original denominator and source hashes are +unchanged. Synthetic misses remain explicit: `super` and inherited fields, +one qualified static-field use, anonymous-class accesses and a local-class +access (six occurrences across five cases). No unexpected registered target was +observed. A separate post-capture check matches all 39 supported contacts to +compiler field/enclosing-method pairs and source ranges; compiler lambda names +map to enclosing source methods. This check assumes the fixture's unambiguous +method names and is not a general overload adapter or blinded precision score. + +Fresh Compass builds use all five pinned read-only repositories; unchanged +Graphify 0.9.67 native graphs are reused. No build-speed comparison is made. +The Chi, Click, Redux and WalkDir graphs are byte-identical to the qualified +Rust-field baseline. jsoup retains all 6,116 nodes and 21,110 previous edges, +adding 3,896 field references for 25,006 edges total. Every added record passes +field-target, exact-identifier, provenance and source-owner checks; 26 initializer +owners require an independent source AST range because the graph's field source +range anchors its declaration token. These are consistency checks, not +compiler-grade target precision across jsoup. The final type correction adds +28 contacts beyond the superseded capture; their receiver declarations and +source expressions were inspected. Existing jsoup/Redux warnings each report +two omitted edges and remain visible. + +### Public retrieval and community impact + +Each tool receives its own exact callable ID and one unfiltered native-default +`get_neighbors` request per Java case, with no retries or source follow-ups. +All eight requests succeed. Compass returns field identity and selected-line +anchors in **4/4**; Graphify returns neither in **0/4**, consistent with its +missing field nodes. Compass's complete nodes and parallel edge records match +the stored graph with no truncation. Aggregate response text is 9,883 versus +3,313 bytes; wire payload is 108,999 versus 3,708 bytes. This is a known-ID +retrieval control, not identity discovery, authored-answer accuracy or an +efficiency win. All 227 Graphify package files verify unchanged. + +jsoup communities change from 41 to 42. Co-member pairs change from 1,110,372 +to 1,077,153: 796,307 retained, 314,065 separated and 280,846 joined. This ignores +numeric community renaming but measures only partition change. Replaying the +unchanged 75 source-selected task pairs gives identical outcomes: within-task +co-location remains Compass 13/15 versus Graphify 12/15; cross-task co-location +remains 18/60 versus 12/60. Cross-task co-location is not automatically wrong. +There is no new community-quality or god-object classification result. + +### Verification and retained evidence + +The final `f2cacfeb` source passes formatting; 27 Java language integration tests; +244 resolver integration tests; 33 cache contracts; workspace and focused-test +Clippy; 1,106 workspace library/binary tests (two ignored); nine CLI product +tests; the product boundary; full production fixture qualification, including +Markdown and the independent React release-binary checks; and all 162 benchmark +tests. The evaluated and qualifying debug binaries match exactly. All validated +production source hashes match the commit. The full fixture gate took 666 +seconds, including a seven-minute release build. Existing fixture omission, +linker and unused-mut warnings remain visible. Hosted CI, full platform, +packaging and browser suites are not claimed. + +AST cache semantics advance from 8 to 9; users rebuild graphs to obtain the +facts. Package version remains 0.3.30; graph/evidence schemas and producer +capabilities are unchanged. Published history is immutable. The committed +`java_state_access_development_review.json` contains result summaries, source +and capture hashes, missing cases and verification commands. External +`java-state-access-02` retains complete graphs, raw public responses, compiler +consistency proofs, failed attempts, deltas and replay scripts. The original +registrations and baseline reports are preserved. Broader target precision, +authored explanations, longer walks, actual god-object judgments and held-out +confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 2499e927a69b7723566b3bc343a9c9b0921c5747 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 06:36:13 -0700 Subject: [PATCH 88/97] Register compiler source-binding audit across jsoup production fields --- .../java_real_field_registration.json | 492 ++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 benchmarks/agent_query/java_real_field_registration.json diff --git a/benchmarks/agent_query/java_real_field_registration.json b/benchmarks/agent_query/java_real_field_registration.json new file mode 100644 index 000000000..d5e243849 --- /dev/null +++ b/benchmarks/agent_query/java_real_field_registration.json @@ -0,0 +1,492 @@ +{ + "schema": "compass.java-real-field-registration/1", + "baselineCommit": "d278c1e9f1e9c99e8a3ca07a7ebd4ceda40f5b63", + "scope": "Complete Java 8 base-source field-reference audit on a previously observed jsoup development repository and frozen graphs. Registered before compiler source binding capture; not held-out or an independent human review. Do not combine with five-language site recall or treat as god-object classification.", + "repository": "jhy/jsoup", + "commit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "sourceRoot": "src/main/java", + "excluded": [ + "package-info.java (metadata compiled separately by project)", + "src/main/java11 overlay (separate build target)", + "src/test (test dependencies and build target outside this panel)" + ], + "buildEvidence": { + "pomSha256": "e100f30fe8dc33f9445644bd5ee53a2e59750995ae62eb209f5882da7c0c63f3", + "release": "8", + "annotationProcessing": false, + "implicitCompilation": false, + "executeProjectCode": false, + "classpath": [ + { + "coordinate": "org.jspecify:jspecify:1.0.1", + "sha256": "070d75f261fe4c5b8202508366715f7f2d4660f88c8ef7e6d3575e48c9683b66" + }, + { + "coordinate": "com.google.re2j:re2j:1.8", + "sha256": "7b52c72156dd7f98b3237a5b35c1d34fba381b21048c89208913ad80a45dfbd7" + } + ] + }, + "files": [ + { + "file": "src/main/java/org/jsoup/Connection.java", + "bytes": 43713, + "sha256": "031151f474574f94a8c9176049a4d8a9f97b14c334cde9ffd1f5b05127acc062" + }, + { + "file": "src/main/java/org/jsoup/HttpStatusException.java", + "bytes": 589, + "sha256": "ecf725c71ae86ab5d915431ce4980b4ffb679d273b1b2561242842fab1ff3196" + }, + { + "file": "src/main/java/org/jsoup/Jsoup.java", + "bytes": 19746, + "sha256": "08efd20ddec51728d05d6aa70091468d6adcd4be703bb578e164012a882c3bf7" + }, + { + "file": "src/main/java/org/jsoup/Progress.java", + "bytes": 802, + "sha256": "edeb649820e6879673415e431cb49db14852b432b55ce0d1858ed359f45293bc" + }, + { + "file": "src/main/java/org/jsoup/SerializationException.java", + "bytes": 1572, + "sha256": "96e426ace0e719074fd26e67ca36bb2657050c133af49b1fd4fba3368e652c59" + }, + { + "file": "src/main/java/org/jsoup/UnsupportedMimeTypeException.java", + "bytes": 689, + "sha256": "ba4fb475e31d400f274d24c1cac751322061be5af6a6b8297df5b489cc6285ac" + }, + { + "file": "src/main/java/org/jsoup/examples/HtmlToPlainText.java", + "bytes": 6484, + "sha256": "3f7e6718b8518240a8d538f60030edeab37648c0b7e70b6c620ee26a434bac7e" + }, + { + "file": "src/main/java/org/jsoup/examples/ListLinks.java", + "bytes": 2032, + "sha256": "a4e4260d8e9671de4fa49d9a0016bec7170631efed2dc5422e4a7583a60cd86e" + }, + { + "file": "src/main/java/org/jsoup/examples/Wikipedia.java", + "bytes": 963, + "sha256": "abf753f2d99f3b5bbd83e55d8af178ebc5a01749a544cb5a0ec1d4f8331a0aa1" + }, + { + "file": "src/main/java/org/jsoup/helper/AuthenticationHandler.java", + "bytes": 3794, + "sha256": "b545635ca593e5df27b62527996980be457ec75745283cc02399d002aa0eb782" + }, + { + "file": "src/main/java/org/jsoup/helper/CookieUtil.java", + "bytes": 5302, + "sha256": "8def4382a79e3ff766d48d29348dca04a5e07e6badee9ad042f4f784518037c2" + }, + { + "file": "src/main/java/org/jsoup/helper/DataUtil.java", + "bytes": 19766, + "sha256": "5ff6d0e32308c350deb8c7541bf430e79caa1eac73a9c9d7b15f4fe90673ea6f" + }, + { + "file": "src/main/java/org/jsoup/helper/HttpConnection.java", + "bytes": 53873, + "sha256": "9f439dc7f7aa03d18a1fbff716cfb562f9e2667b249a60dfc329f5232b3c1145" + }, + { + "file": "src/main/java/org/jsoup/helper/Re2jRegex.java", + "bytes": 1745, + "sha256": "1ee3361ff0b7a48e9332ed96780162d4601c3f7c392f189b3d4cef3164a4c9f5" + }, + { + "file": "src/main/java/org/jsoup/helper/Regex.java", + "bytes": 4248, + "sha256": "50b30ad1a0cdd14d27070407f577f27397bbaedb2baf7e98ecd813c79b89eee0" + }, + { + "file": "src/main/java/org/jsoup/helper/RequestAuthenticator.java", + "bytes": 2934, + "sha256": "52ddc822692de5b5f21ee161d5743e18bbb3a2b24aebd0714a77f8a1f9ef69dd" + }, + { + "file": "src/main/java/org/jsoup/helper/RequestDispatch.java", + "bytes": 2043, + "sha256": "8f6f8c50b1126e6b4f3c48e67b861f2df60158ff61ec79e85590f968d3f4dc8c" + }, + { + "file": "src/main/java/org/jsoup/helper/RequestExecutor.java", + "bytes": 749, + "sha256": "666b1e382faebcb196e9cd351607455fe29592e347dcb47a4f6f9f66f745c01e" + }, + { + "file": "src/main/java/org/jsoup/helper/UrlBuilder.java", + "bytes": 4426, + "sha256": "874ee79c27016735ff8a5141e1f09f76be40438e1f9bec99311b9a34a6e6f593" + }, + { + "file": "src/main/java/org/jsoup/helper/UrlConnectionExecutor.java", + "bytes": 4993, + "sha256": "886011395e27d3922686ae88fc7fe305c2a512d11c9770cc7de1887d4f17998c" + }, + { + "file": "src/main/java/org/jsoup/helper/Validate.java", + "bytes": 7084, + "sha256": "256262c372b996305f30d89e668cb5110d6b36b13afa551c8bfa3a5ee50c85e6" + }, + { + "file": "src/main/java/org/jsoup/helper/ValidationException.java", + "bytes": 972, + "sha256": "209e9b62845a9effe55d9e684c8ebe76d6f63be27cd66ee0a2ff24ce3585cfbd" + }, + { + "file": "src/main/java/org/jsoup/helper/W3CDom.java", + "bytes": 27090, + "sha256": "97221e9770c46f56ad4736fccc0ec5fd690f9708ded17537e59fcd9faf137725" + }, + { + "file": "src/main/java/org/jsoup/internal/ControllableInputStream.java", + "bytes": 12748, + "sha256": "8b1561d4df241a4ce77ce009df23e7e604763b35f2700f9ba2e4a23b08bae1eb" + }, + { + "file": "src/main/java/org/jsoup/internal/LineMap.java", + "bytes": 2378, + "sha256": "a512f223f1f9c2ada0abb37b0342d2cfbc91181d3593b2d9035b6287e3c567a0" + }, + { + "file": "src/main/java/org/jsoup/internal/NamespaceBindings.java", + "bytes": 3581, + "sha256": "10ec33f8ed69f55cac30042a59b5508068513466f352eab85b9ea40f71dafca5" + }, + { + "file": "src/main/java/org/jsoup/internal/Normalizer.java", + "bytes": 1790, + "sha256": "5587cbbc0574eebd72f2ef03442047f36f924ae4ec95347fc24aee0c6ab50185" + }, + { + "file": "src/main/java/org/jsoup/internal/QuietAppendable.java", + "bytes": 2637, + "sha256": "7ddaaa971e97c710c34c5a824d40583c8a452297cec2668d801eaa4f9de4dc9f" + }, + { + "file": "src/main/java/org/jsoup/internal/SharedConstants.java", + "bytes": 1013, + "sha256": "efea92b5536f08bbb85c1ee3233d8a3fa4cac854f49413a816fc9dd488f72e17" + }, + { + "file": "src/main/java/org/jsoup/internal/SimpleBufferedInput.java", + "bytes": 6768, + "sha256": "5456a093bb2da2fdbde7775182fb364ee7a44ddaa2b76e6d0410d8962ba2a525" + }, + { + "file": "src/main/java/org/jsoup/internal/SimpleStreamReader.java", + "bytes": 3412, + "sha256": "26daa659178efc7edde8f2b58e0b862ed68d96e9adacaf8a3ed0ab6eddd4968e" + }, + { + "file": "src/main/java/org/jsoup/internal/SoftPool.java", + "bytes": 2400, + "sha256": "f2e1f04034ff027f342fe1863532702aefa2aabe77ce425b96f1896f7de01e08" + }, + { + "file": "src/main/java/org/jsoup/internal/StringUtil.java", + "bytes": 17505, + "sha256": "7e61ba7e8630f101f0a74d56fb91237c8588fceeb5c472166c60926dd6047881" + }, + { + "file": "src/main/java/org/jsoup/nodes/Attribute.java", + "bytes": 12105, + "sha256": "3d747f3fad35d3f9dfc0f8466a45c94b21e362cc553d8190119eac4047315739" + }, + { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "bytes": 26795, + "sha256": "39551b1d008f2a77b212b5b16e09e257b0ec9d8f118a0b7131a1cfe11ac32875" + }, + { + "file": "src/main/java/org/jsoup/nodes/CDataNode.java", + "bytes": 841, + "sha256": "07f8c6622992fa6af68516d09cf7cf1a25b0006aa69e90a6208a7b90a53b32d9" + }, + { + "file": "src/main/java/org/jsoup/nodes/Comment.java", + "bytes": 2255, + "sha256": "311956f0cd15ecacca663f56a27f9bbb18beb71968aa7b7171bd81831c93d3e8" + }, + { + "file": "src/main/java/org/jsoup/nodes/DataNode.java", + "bytes": 1956, + "sha256": "045c6c94ff716e666b842e79ee961c923559b89d3ca51c2e174ea9f4e63c0848" + }, + { + "file": "src/main/java/org/jsoup/nodes/Document.java", + "bytes": 20793, + "sha256": "cad397b7c5c767c294f27edf7971932c0d531ae8a368827e6ec34e1267a17502" + }, + { + "file": "src/main/java/org/jsoup/nodes/DocumentType.java", + "bytes": 3854, + "sha256": "7a37c3ef20e42e53ea53a2faa1045155f05f938a74ab153007900e37a3f64664" + }, + { + "file": "src/main/java/org/jsoup/nodes/Element.java", + "bytes": 84173, + "sha256": "64113061dec074483ca4bcf51a528ca55e511f81da2192e44e626347cc9c6340" + }, + { + "file": "src/main/java/org/jsoup/nodes/Entities.java", + "bytes": 17614, + "sha256": "5192dae7df7bc05b3c6346f8db8c7bb45f5084e45f6d6ac17f9f5791b2555bcc" + }, + { + "file": "src/main/java/org/jsoup/nodes/EntitiesData.java", + "bytes": 33565, + "sha256": "5d2d6726a607fca5d0f9393aa6d5dc0882e780507dfb35371cd4d17e7e1e63f9" + }, + { + "file": "src/main/java/org/jsoup/nodes/FormElement.java", + "bytes": 5802, + "sha256": "400e23f8f7ede595e2063bb1611bc796de6d7b920fd835f11ffb4afc6b1ab6e5" + }, + { + "file": "src/main/java/org/jsoup/nodes/LeafNode.java", + "bytes": 5709, + "sha256": "a034f109630e48e6c225e15737288c8e27b12d370dd103fc945bf83c8a89edba" + }, + { + "file": "src/main/java/org/jsoup/nodes/Node.java", + "bytes": 38462, + "sha256": "5a8e21930be1a65ae41055a09ba3400dd8a134ad3e693cea61f41cc4242d27ec" + }, + { + "file": "src/main/java/org/jsoup/nodes/NodeInternals.java", + "bytes": 2565, + "sha256": "3d593987076236b10c89ce8c3b6ebf3103fba56338cee9c7180255ce9795eda9" + }, + { + "file": "src/main/java/org/jsoup/nodes/NodeIterator.java", + "bytes": 4503, + "sha256": "6f5b0b36d110f6c664016bbfc0a11b3eb6af01d441b20f230a90839b9dc18bab" + }, + { + "file": "src/main/java/org/jsoup/nodes/NodeUtils.java", + "bytes": 2760, + "sha256": "04dffffb32b1fa4bfeb237205faa6e37433cf773ecc57a9e89de3f9d1eb67bd9" + }, + { + "file": "src/main/java/org/jsoup/nodes/Printer.java", + "bytes": 9607, + "sha256": "63972e16a946056b46d1cc6b92bfc4df3c5c6e559b865a4ba21c1a0a23452de5" + }, + { + "file": "src/main/java/org/jsoup/nodes/ProcessingInstruction.java", + "bytes": 5195, + "sha256": "08ac02e2561950fc907ff4481099932aab5e0d9acf77ea59da599cfb001e3974" + }, + { + "file": "src/main/java/org/jsoup/nodes/Range.java", + "bytes": 18868, + "sha256": "5e5365018f097d63eba66eb24d84dfd0a7e1d623ec9d9b20a86b694c0eb9d68b" + }, + { + "file": "src/main/java/org/jsoup/nodes/TextNode.java", + "bytes": 3584, + "sha256": "6b524fbd7fc85e56ad19fd5b45beb2bd72f299a30441c423bdcdca71837a12a8" + }, + { + "file": "src/main/java/org/jsoup/nodes/XmlDeclaration.java", + "bytes": 2899, + "sha256": "622cf1a566bf747593815d3d39b6f91406f4a73f738caf79886675ef14d53ad5" + }, + { + "file": "src/main/java/org/jsoup/parser/CharacterReader.java", + "bytes": 23540, + "sha256": "f93452a6022d438df7ce17f00dabd9d32589815b30ee4f5e6d1bd1208e36274c" + }, + { + "file": "src/main/java/org/jsoup/parser/ForeignNames.java", + "bytes": 3610, + "sha256": "1239f1386994cc399195f8e564a39327b0029ad8a7381066a57536c91f4e48d7" + }, + { + "file": "src/main/java/org/jsoup/parser/HtmlTagOptions.java", + "bytes": 3893, + "sha256": "ecf9e4de64b525014c62ef46e7fda0cd5f45c9afa39b57143134c29d301c98e1" + }, + { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilder.java", + "bytes": 50484, + "sha256": "c7fff264056bc226eb6065efd407cbf990722c739afc00ebcde1ffef6ca7b72f" + }, + { + "file": "src/main/java/org/jsoup/parser/HtmlTreeBuilderState.java", + "bytes": 82032, + "sha256": "53a24a75eea9a75c941bdfa7240188ec5f6d79e0ad9c771c0ad4233ef331b763" + }, + { + "file": "src/main/java/org/jsoup/parser/ParseError.java", + "bytes": 1617, + "sha256": "73a1dbe9caf2a439051b5dd77af383ae339d034d24ec673f9634d092d23aa6f0" + }, + { + "file": "src/main/java/org/jsoup/parser/ParseErrorList.java", + "bytes": 1241, + "sha256": "c4aa6c789ee9044cb74ca35255bfd7030b1f45b6f38a4da7744bc48bd6efcea3" + }, + { + "file": "src/main/java/org/jsoup/parser/ParseSettings.java", + "bytes": 2258, + "sha256": "733903589b1f65f000b04b969b4d3dd462754b2b9b3b94f1342f5b284b3af3fd" + }, + { + "file": "src/main/java/org/jsoup/parser/Parser.java", + "bytes": 15426, + "sha256": "2b8baa95140fbf12fad9c874b42d8d31e24b186fd7c665c1708395e6d852ba98" + }, + { + "file": "src/main/java/org/jsoup/parser/StreamParser.java", + "bytes": 19831, + "sha256": "5a50cf810031c426623c68cc630218c3e6725e1519bb039143777f9f19637855" + }, + { + "file": "src/main/java/org/jsoup/parser/Tag.java", + "bytes": 13340, + "sha256": "98b0a56d5963298da2176eeb555ae1e66cf43ddd0df28a25ebec1fa8ed7bee5a" + }, + { + "file": "src/main/java/org/jsoup/parser/TagSet.java", + "bytes": 13030, + "sha256": "26261d2c9ee3d2e978c184f15328022488af3264ae8b1c54b6bd8fcdd02a0dff" + }, + { + "file": "src/main/java/org/jsoup/parser/Token.java", + "bytes": 21265, + "sha256": "a2c94fbf931bcb7fae31da45ca56a3070a7c1ed3ec4d82c9839b6b120595a379" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenData.java", + "bytes": 2419, + "sha256": "816b9225ced88924eb45b7a0dccea19f4a15e96154a9700f34fad3a3435e4bfb" + }, + { + "file": "src/main/java/org/jsoup/parser/TokenQueue.java", + "bytes": 17091, + "sha256": "7a23106abcfc95d71a6b774bdc10c02d33a98b25cbc429ade2d94db5d69e0fb1" + }, + { + "file": "src/main/java/org/jsoup/parser/Tokeniser.java", + "bytes": 16428, + "sha256": "16250f9ef5cf94e3fc7100a8356e466ef1c4273d84c455d64ace82a29b35a09f" + }, + { + "file": "src/main/java/org/jsoup/parser/TokeniserState.java", + "bytes": 70346, + "sha256": "d6a94fc73b4561011470263872b74b29024ed1ddddf220df636357a1ab35bea0" + }, + { + "file": "src/main/java/org/jsoup/parser/TreeBuilder.java", + "bytes": 13876, + "sha256": "6f210b4b6e1f5e5d1f81655aa279103bf101e5b1f0ec17ee8fe7035894c10746" + }, + { + "file": "src/main/java/org/jsoup/parser/XmlTreeBuilder.java", + "bytes": 9735, + "sha256": "c016f43e79dbf01b49370d5ba689ebbdb70f3d15e108d9d0f75eacf1e6a88abd" + }, + { + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "bytes": 11402, + "sha256": "7116b40cb8be432493dff5eea6cee4e774867b08c04cc04157b8137d97102a0d" + }, + { + "file": "src/main/java/org/jsoup/safety/Safelist.java", + "bytes": 27741, + "sha256": "0ff250830902fd2a7d7a2c6f5e3cd1eb851ef0c57123f71db6afaf5711990501" + }, + { + "file": "src/main/java/org/jsoup/select/Collector.java", + "bytes": 4122, + "sha256": "84a60695159cdd345b6ec5f19ea968ba9d0951956c7d9ad0ae97a7ab01e009c5" + }, + { + "file": "src/main/java/org/jsoup/select/CombiningEvaluator.java", + "bytes": 4728, + "sha256": "4d7d2aa47151f1be31769709738db73afafe65458871b817510925327c4fe1c8" + }, + { + "file": "src/main/java/org/jsoup/select/Elements.java", + "bytes": 25504, + "sha256": "b6ed7b3905a52d85658ba6e6b1347de84513b9c6b51e8c8f5879ee29865f2873" + }, + { + "file": "src/main/java/org/jsoup/select/Evaluator.java", + "bytes": 29101, + "sha256": "5a3ce0742ddf5d5bb647e8627735c47c656f5642361f41fbb30f26fa7b6f7e8d" + }, + { + "file": "src/main/java/org/jsoup/select/HasEvaluator.java", + "bytes": 7033, + "sha256": "64e5e7227c38b9663c3dab4fb152c17ffc7ef9b155f42a2b99f9b57b1307e321" + }, + { + "file": "src/main/java/org/jsoup/select/NodeEvaluator.java", + "bytes": 3550, + "sha256": "7099d38531322df9b7a3c6a12995f891aaa37620d4b21b26a3017d8721dbcc7e" + }, + { + "file": "src/main/java/org/jsoup/select/NodeFilter.java", + "bytes": 2682, + "sha256": "d6902c3debda319f47a7df5b281a2544154bf692c57b3a35af248456fb3a4234" + }, + { + "file": "src/main/java/org/jsoup/select/NodeTraversor.java", + "bytes": 7364, + "sha256": "3fc9d4e257300e2399bf61d631838411539936e733adbec458a483f2b3b1e235" + }, + { + "file": "src/main/java/org/jsoup/select/NodeVisitor.java", + "bytes": 3748, + "sha256": "f6b26808cd5acd748746a94bc45935a63776ad7dcc38f5f2d80c04375aae773d" + }, + { + "file": "src/main/java/org/jsoup/select/Nodes.java", + "bytes": 9816, + "sha256": "d4c8c088df00bf04e5c9cd7e47bda7b644a52552bf11387870f882f07b0ac7f2" + }, + { + "file": "src/main/java/org/jsoup/select/QueryParser.java", + "bytes": 27791, + "sha256": "86eb0671a0c358d12f75bbeea6da4fe4525dfa879ce339d25e27ba58f66a58d3" + }, + { + "file": "src/main/java/org/jsoup/select/Selector.java", + "bytes": 23604, + "sha256": "5452b5f12561dedcb8b7dccfdf861c4159e1e4292fef6a2d0cb4f1636f8a84b9" + }, + { + "file": "src/main/java/org/jsoup/select/StructuralEvaluator.java", + "bytes": 7243, + "sha256": "5e67d4df5224e8e19ecf8415d50ac19c1ae19889cb98823fa4ca9028e040bd16" + } + ], + "graphRun": "java-state-access-02/run.json", + "graphRunSha256": "c315bffe56cbb7a7684c088e0219fc85ba9ef0681a39463608845691522d21ff", + "graphSha256": { + "compass": "2a9216926b1c5dfc82e4f88df0cce24a7a14ac67aa2a350158eff39018145314", + "graphify": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69" + }, + "protocol": { + "oracle": "JDK JavacTask parse/analyze and Trees source bindings. No code generation or project execution. Error diagnostics abort scored capture. Enumerate all variable field/enum-constant expression references, retaining target declaration and enclosing source owner. Exclude import bindings and synthetic this/super identifiers; distinguish external/intrinsic targets and unsupported anchors.", + "identity": "Join graph declarations to compiler source file, declaration region and name; preserve multiple candidates as ambiguous. Count exact UTF-8 occurrence spans, target identity and caller identity separately. No first-match or nearest-name selection. Report all graph field contacts inside registered files, including unmatched/wrong targets and owners.", + "comparison": "Use the same frozen source inputs and native graphs for both tools. Report full-source occurrence recall and returned-contact precision only when the adapter independently verifies identities; absent fields are misses. Separate ordinary fields and enum constants; preserve unsupported cases and negative outcomes. No graph rebuild or performance comparison.", + "qualification": "First check compiler oracle against existing 44-case source/bytecode fixture and additional bounded adversarial fixtures for Unicode positions, shadowing, hidden fields, lambdas, anonymous/local classes, overload ownership and multi-declarators. Do not tune product implementation in the baseline capture.", + "bounds": { + "files": 512, + "fileBytes": 4194304, + "totalSourceBytes": 67108864, + "records": 100000, + "seconds": 120, + "heapMiB": 1024 + } + } +} From 7b9d46f17f876c4c1414b4c08cdda80aaa07b82c Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:06:33 -0700 Subject: [PATCH 89/97] Audit Java field bindings against the full jsoup base-source census --- benchmarks/agent_query/README.md | 58 ++++ .../java_field_bindings/BindingFixture.java | 38 +++ .../adversarial-manifest.json | 8 + .../java_field_bindings/adversarial.jsonl | 53 ++++ .../java_field_bindings/scope-manifest.json | 8 + .../fixtures/java_field_bindings/scope.jsonl | 105 +++++++ benchmarks/agent_query/java_field_capture.py | 100 ++++++ .../java_oracle/FieldBindings.java | 221 +++++++++++++ .../agent_query/java_real_field_review.json | 209 +++++++++++++ benchmarks/agent_query/java_source_fields.py | 290 ++++++++++++++++++ .../tests/test_java_source_fields.py | 169 ++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 77 +++++ 12 files changed, 1336 insertions(+) create mode 100644 benchmarks/agent_query/fixtures/java_field_bindings/BindingFixture.java create mode 100644 benchmarks/agent_query/fixtures/java_field_bindings/adversarial-manifest.json create mode 100644 benchmarks/agent_query/fixtures/java_field_bindings/adversarial.jsonl create mode 100644 benchmarks/agent_query/fixtures/java_field_bindings/scope-manifest.json create mode 100644 benchmarks/agent_query/fixtures/java_field_bindings/scope.jsonl create mode 100644 benchmarks/agent_query/java_field_capture.py create mode 100644 benchmarks/agent_query/java_oracle/FieldBindings.java create mode 100644 benchmarks/agent_query/java_real_field_review.json create mode 100644 benchmarks/agent_query/java_source_fields.py create mode 100644 benchmarks/agent_query/tests/test_java_source_fields.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index e3bab2989..2b07325cd 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -600,3 +600,61 @@ Compass's complete response payload is substantially larger. The unchanged not establish authored explanations, cohesion, god-object defects, exhaustive edge precision or overall superiority. See the audit document for remaining misses, payload costs, partition changes, warnings and exact verification. + +### Compiler source-binding census for real Java fields + +`java_real_field_registration.json` freezes all 88 Java 8 base-source files in +jsoup before compiler binding capture. It retains the existing native graph +hashes, project source pin, build configuration and cached dependency digests. +`java_real_field_review.json` records the comparison and all remaining misses. +This previously observed development repository is not held-out evidence. + +The public JDK `JavacTask`/`Trees` oracle parses and attributes source without +code generation, annotation processing or project execution. Source positions +are converted from UTF-16 to UTF-8 with split-surrogate checks. The auditor +joins source declarations without choosing between ambiguous candidates and +requires exact occurrence, target and source-owner evidence for credit. It +inventories graph contacts even when their identity or anchor cannot be verified; +line-only and unordered contacts are not upgraded to exact/directed evidence. + +Use an installed JDK 17 and the digest-matched cached dependencies named by the +registration. Every capture requires a new external artifact directory: + +```sh +python3 -m benchmarks.agent_query.java_field_capture \ + --registration benchmarks/agent_query/java_real_field_registration.json \ + --root /Volumes/Workspace/Github/jhy/jsoup \ + --java-home /path/to/installed/jdk17 \ + --classpath /path/to/jspecify-1.0.1.jar \ + --classpath /path/to/re2j-1.8.jar \ + --artifacts /Volumes/Workspace/CrabData/java-field-capture-new +python3 -m benchmarks.agent_query.java_source_fields \ + --capture /Volumes/Workspace/CrabData/java-field-capture-new/bindings.stdout \ + --manifest /Volumes/Workspace/CrabData/java-field-capture-new/manifest.json \ + --root /Volumes/Workspace/Github/jhy/jsoup \ + --graph /path/to/frozen-graph.json --tool compass \ + --output /Volumes/Workspace/CrabData/java-field-review-new.json +``` + +Use `--tool graphify` with its native graph. Add `--verify` to replay an existing +review; default output creation rejects overwrites. Offline replay and unit tests +need no JDK. Compiler errors, incomplete output, source drift and limits fail the +capture; they never become empty successful inventories. + +The final census contains 3,785 ordinary source-field references and 529 enum +constant references. Compass verifies 3,047 ordinary references and all 3,047 +returned contacts in scope; Graphify has no field-contact records. Both tools +represent all 131 enum constants but miss all 529 reference occurrences. Compass +represents 614/616 ordinary fields; Graphify represents none. The remaining +44 external fields, 55 array lengths and 31 class literals are reported +separately. Arrays and class literals are javac pseudo-fields, not source fields. + +All 44 previous scope cases and their 45 bytecode-checked occurrences agree with +this source oracle. The additional fixture covers overload ownership, Unicode, +compound uses, constant folding, intrinsics, initializers and anonymous/local +classes. All 176 auditor tests pass. Repeated full captures and offline reviews +are identical. External `jsoup-java-field-oracle-02` retains final evidence; +round 01's mixed non-source category remains explicitly superseded. These results +strengthen source-declaration precision evidence for this configuration; they +do not score read/write effects, explanations, paths, community quality or +actual god-object defects. diff --git a/benchmarks/agent_query/fixtures/java_field_bindings/BindingFixture.java b/benchmarks/agent_query/fixtures/java_field_bindings/BindingFixture.java new file mode 100644 index 000000000..11def8775 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_field_bindings/BindingFixture.java @@ -0,0 +1,38 @@ +package bindings; +import static java.lang.Integer.MAX_VALUE; + +// UTF-16 and UTF-8 offsets differ after λ and 🧭. +public class BindingFixture extends Base { + int left, right; + static final int LIMIT = 3; + String label = "λ🧭"; + int initial = left; // expect left; owner initial + { right = left; } // expect right,left; owner BindingFixture + int read(int seed) { return left; } // expect left; overload 1 + int read(String seed) { return right; } // expect right; overload 2 + int compound() { left += right; return left; } // expect left,right,left + int constant() { return LIMIT; } // expect LIMIT despite bytecode folding + int external() { return MAX_VALUE; } // external field, no source declaration + int commented(BindingFixture self) { return self./* comment */left; } // expect left + int \u0078; + int escaped() { return \u0078; } // expect x with unsupported raw spelling anchor + java.util.function.IntSupplier lambda() { return () -> left; } // expect left; owner lambda + int hidden() { return super.left + left; } // expect Base.left,left + Object anonymous() { + return new Object() { + int left; + int get() { return left; } // expect anonymous left; owner get + }; + } + int local() { + class Local { + int left; + int get() { return left; } // expect local left; owner get + } + return new Local().get(); + } + enum Token { A, B } + Token token() { return Token.A; } // expect enum constant A + int intrinsic(int[] values) { return values.length + BindingFixture.class.getName().length(); } // two intrinsics +} +class Base { int left; } diff --git a/benchmarks/agent_query/fixtures/java_field_bindings/adversarial-manifest.json b/benchmarks/agent_query/fixtures/java_field_bindings/adversarial-manifest.json new file mode 100644 index 000000000..a3f77d719 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_field_bindings/adversarial-manifest.json @@ -0,0 +1,8 @@ +{ + "files": { + "BindingFixture.java": "3c6af5eee01529024a68fa9822cf882d60791b860eb164e180329c8e2eb1f19b" + }, + "captureSha256": "be9b847db2aa9a8a298c9fe2c91453250e3c8b2d8ba89dbca87c787e6f5f7967", + "toolSha256": "0616d29730f77fc33a313e5c3280d6905c5435433f8e52ac4250ce2a8124e9d6", + "compilerRuntime": "17.0.8.1+8-LTS" +} diff --git a/benchmarks/agent_query/fixtures/java_field_bindings/adversarial.jsonl b/benchmarks/agent_query/fixtures/java_field_bindings/adversarial.jsonl new file mode 100644 index 000000000..dc807d864 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_field_bindings/adversarial.jsonl @@ -0,0 +1,53 @@ +{"type":"header","schema":"compass.javac-field-bindings/1","files":1,"sourceBytes":1680,"release":"17","positionEncoding":"UTF-16 code units","compilerRuntime":"17.0.8.1+8-LTS"} +{"type":"declaration","declaration":{"id":"BindingFixture.java:113:1648:class:BindingFixture","kind":"class","file":"BindingFixture.java","name":"BindingFixture","qualified":"bindings.BindingFixture","startUtf16":113,"endUtf16":1648,"startLine":5,"endLine":37}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:160:176:field:right","kind":"field","file":"BindingFixture.java","name":"right","qualified":"bindings.BindingFixture::right","startUtf16":160,"endUtf16":176,"startLine":6,"endLine":6}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:181:208:field:LIMIT","kind":"field","file":"BindingFixture.java","name":"LIMIT","qualified":"bindings.BindingFixture::LIMIT","startUtf16":181,"endUtf16":208,"startLine":7,"endLine":7}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:213:234:field:label","kind":"field","file":"BindingFixture.java","name":"label","qualified":"bindings.BindingFixture::label","startUtf16":213,"endUtf16":234,"startLine":8,"endLine":8}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:239:258:field:initial","kind":"field","file":"BindingFixture.java","name":"initial","qualified":"bindings.BindingFixture::initial","startUtf16":239,"endUtf16":258,"startLine":9,"endLine":9}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":253,"expressionEndUtf16":257,"tokenStartUtf16":253,"tokenEndUtf16":257,"anchorStatus":"exact","line":9,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:239:258:field:initial","kind":"field","file":"BindingFixture.java","name":"initial","qualified":"bindings.BindingFixture::initial","startUtf16":239,"endUtf16":258,"startLine":9,"endLine":9}} +{"type":"fieldReference","file":"BindingFixture.java","name":"right","expressionStartUtf16":295,"expressionEndUtf16":300,"tokenStartUtf16":295,"tokenEndUtf16":300,"anchorStatus":"exact","line":10,"targetKind":"field","targetQualified":"bindings.BindingFixture::right","targetOrigin":"source","target":{"id":"BindingFixture.java:160:176:field:right","kind":"field","file":"BindingFixture.java","name":"right","qualified":"bindings.BindingFixture::right","startUtf16":160,"endUtf16":176,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:113:1648:class:BindingFixture","kind":"class","file":"BindingFixture.java","name":"BindingFixture","qualified":"bindings.BindingFixture","startUtf16":113,"endUtf16":1648,"startLine":5,"endLine":37}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":303,"expressionEndUtf16":307,"tokenStartUtf16":303,"tokenEndUtf16":307,"anchorStatus":"exact","line":10,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:113:1648:class:BindingFixture","kind":"class","file":"BindingFixture.java","name":"BindingFixture","qualified":"bindings.BindingFixture","startUtf16":113,"endUtf16":1648,"startLine":5,"endLine":37}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:358:393:method:read","kind":"method","file":"BindingFixture.java","name":"read","qualified":"bindings.BindingFixture::read","startUtf16":358,"endUtf16":393,"startLine":11,"endLine":11}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":386,"expressionEndUtf16":390,"tokenStartUtf16":386,"tokenEndUtf16":390,"anchorStatus":"exact","line":11,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:358:393:method:read","kind":"method","file":"BindingFixture.java","name":"read","qualified":"bindings.BindingFixture::read","startUtf16":358,"endUtf16":393,"startLine":11,"endLine":11}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:425:464:method:read","kind":"method","file":"BindingFixture.java","name":"read","qualified":"bindings.BindingFixture::read","startUtf16":425,"endUtf16":464,"startLine":12,"endLine":12}} +{"type":"fieldReference","file":"BindingFixture.java","name":"right","expressionStartUtf16":456,"expressionEndUtf16":461,"tokenStartUtf16":456,"tokenEndUtf16":461,"anchorStatus":"exact","line":12,"targetKind":"field","targetQualified":"bindings.BindingFixture::right","targetOrigin":"source","target":{"id":"BindingFixture.java:160:176:field:right","kind":"field","file":"BindingFixture.java","name":"right","qualified":"bindings.BindingFixture::right","startUtf16":160,"endUtf16":176,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:425:464:method:read","kind":"method","file":"BindingFixture.java","name":"read","qualified":"bindings.BindingFixture::read","startUtf16":425,"endUtf16":464,"startLine":12,"endLine":12}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:497:543:method:compound","kind":"method","file":"BindingFixture.java","name":"compound","qualified":"bindings.BindingFixture::compound","startUtf16":497,"endUtf16":543,"startLine":13,"endLine":13}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":514,"expressionEndUtf16":518,"tokenStartUtf16":514,"tokenEndUtf16":518,"anchorStatus":"exact","line":13,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:497:543:method:compound","kind":"method","file":"BindingFixture.java","name":"compound","qualified":"bindings.BindingFixture::compound","startUtf16":497,"endUtf16":543,"startLine":13,"endLine":13}} +{"type":"fieldReference","file":"BindingFixture.java","name":"right","expressionStartUtf16":522,"expressionEndUtf16":527,"tokenStartUtf16":522,"tokenEndUtf16":527,"anchorStatus":"exact","line":13,"targetKind":"field","targetQualified":"bindings.BindingFixture::right","targetOrigin":"source","target":{"id":"BindingFixture.java:160:176:field:right","kind":"field","file":"BindingFixture.java","name":"right","qualified":"bindings.BindingFixture::right","startUtf16":160,"endUtf16":176,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:497:543:method:compound","kind":"method","file":"BindingFixture.java","name":"compound","qualified":"bindings.BindingFixture::compound","startUtf16":497,"endUtf16":543,"startLine":13,"endLine":13}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":536,"expressionEndUtf16":540,"tokenStartUtf16":536,"tokenEndUtf16":540,"anchorStatus":"exact","line":13,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:497:543:method:compound","kind":"method","file":"BindingFixture.java","name":"compound","qualified":"bindings.BindingFixture::compound","startUtf16":497,"endUtf16":543,"startLine":13,"endLine":13}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:574:606:method:constant","kind":"method","file":"BindingFixture.java","name":"constant","qualified":"bindings.BindingFixture::constant","startUtf16":574,"endUtf16":606,"startLine":14,"endLine":14}} +{"type":"fieldReference","file":"BindingFixture.java","name":"LIMIT","expressionStartUtf16":598,"expressionEndUtf16":603,"tokenStartUtf16":598,"tokenEndUtf16":603,"anchorStatus":"exact","line":14,"targetKind":"field","targetQualified":"bindings.BindingFixture::LIMIT","targetOrigin":"source","target":{"id":"BindingFixture.java:181:208:field:LIMIT","kind":"field","file":"BindingFixture.java","name":"LIMIT","qualified":"bindings.BindingFixture::LIMIT","startUtf16":181,"endUtf16":208,"startLine":7,"endLine":7},"owner":{"id":"BindingFixture.java:574:606:method:constant","kind":"method","file":"BindingFixture.java","name":"constant","qualified":"bindings.BindingFixture::constant","startUtf16":574,"endUtf16":606,"startLine":14,"endLine":14}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:652:688:method:external","kind":"method","file":"BindingFixture.java","name":"external","qualified":"bindings.BindingFixture::external","startUtf16":652,"endUtf16":688,"startLine":15,"endLine":15}} +{"type":"fieldReference","file":"BindingFixture.java","name":"MAX_VALUE","expressionStartUtf16":676,"expressionEndUtf16":685,"tokenStartUtf16":676,"tokenEndUtf16":685,"anchorStatus":"exact","line":15,"targetKind":"field","targetQualified":"java.lang.Integer::MAX_VALUE","targetOrigin":"external","target":null,"owner":{"id":"BindingFixture.java:652:688:method:external","kind":"method","file":"BindingFixture.java","name":"external","qualified":"bindings.BindingFixture::external","startUtf16":652,"endUtf16":688,"startLine":15,"endLine":15}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:734:803:method:commented","kind":"method","file":"BindingFixture.java","name":"commented","qualified":"bindings.BindingFixture::commented","startUtf16":734,"endUtf16":803,"startLine":16,"endLine":16}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":778,"expressionEndUtf16":800,"tokenStartUtf16":796,"tokenEndUtf16":800,"anchorStatus":"exact","line":16,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:734:803:method:commented","kind":"method","file":"BindingFixture.java","name":"commented","qualified":"bindings.BindingFixture::commented","startUtf16":734,"endUtf16":803,"startLine":16,"endLine":16}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:823:834:field:x","kind":"field","file":"BindingFixture.java","name":"x","qualified":"bindings.BindingFixture::x","startUtf16":823,"endUtf16":834,"startLine":17,"endLine":17}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:839:871:method:escaped","kind":"method","file":"BindingFixture.java","name":"escaped","qualified":"bindings.BindingFixture::escaped","startUtf16":839,"endUtf16":871,"startLine":18,"endLine":18}} +{"type":"fieldReference","file":"BindingFixture.java","name":"x","expressionStartUtf16":862,"expressionEndUtf16":868,"tokenStartUtf16":null,"tokenEndUtf16":null,"anchorStatus":"unsupported-raw-spelling","line":18,"targetKind":"field","targetQualified":"bindings.BindingFixture::x","targetOrigin":"source","target":{"id":"BindingFixture.java:823:834:field:x","kind":"field","file":"BindingFixture.java","name":"x","qualified":"bindings.BindingFixture::x","startUtf16":823,"endUtf16":834,"startLine":17,"endLine":17},"owner":{"id":"BindingFixture.java:839:871:method:escaped","kind":"method","file":"BindingFixture.java","name":"escaped","qualified":"bindings.BindingFixture::escaped","startUtf16":839,"endUtf16":871,"startLine":18,"endLine":18}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:925:987:method:lambda","kind":"method","file":"BindingFixture.java","name":"lambda","qualified":"bindings.BindingFixture::lambda","startUtf16":925,"endUtf16":987,"startLine":19,"endLine":19}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":980,"expressionEndUtf16":984,"tokenStartUtf16":980,"tokenEndUtf16":984,"anchorStatus":"exact","line":19,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:925:987:method:lambda","kind":"method","file":"BindingFixture.java","name":"lambda","qualified":"bindings.BindingFixture::lambda","startUtf16":925,"endUtf16":987,"startLine":19,"endLine":19}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1021:1063:method:hidden","kind":"method","file":"BindingFixture.java","name":"hidden","qualified":"bindings.BindingFixture::hidden","startUtf16":1021,"endUtf16":1063,"startLine":20,"endLine":20}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":1043,"expressionEndUtf16":1053,"tokenStartUtf16":1049,"tokenEndUtf16":1053,"anchorStatus":"exact","line":20,"targetKind":"field","targetQualified":"bindings.Base::left","targetOrigin":"source","target":{"id":"BindingFixture.java:1662:1671:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.Base::left","startUtf16":1662,"endUtf16":1671,"startLine":38,"endLine":38},"owner":{"id":"BindingFixture.java:1021:1063:method:hidden","kind":"method","file":"BindingFixture.java","name":"hidden","qualified":"bindings.BindingFixture::hidden","startUtf16":1021,"endUtf16":1063,"startLine":20,"endLine":20}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":1056,"expressionEndUtf16":1060,"tokenStartUtf16":1056,"tokenEndUtf16":1060,"anchorStatus":"exact","line":20,"targetKind":"field","targetQualified":"bindings.BindingFixture::left","targetOrigin":"source","target":{"id":"BindingFixture.java:160:169:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.BindingFixture::left","startUtf16":160,"endUtf16":169,"startLine":6,"endLine":6},"owner":{"id":"BindingFixture.java:1021:1063:method:hidden","kind":"method","file":"BindingFixture.java","name":"hidden","qualified":"bindings.BindingFixture::hidden","startUtf16":1021,"endUtf16":1063,"startLine":20,"endLine":20}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1093:1257:method:anonymous","kind":"method","file":"BindingFixture.java","name":"anonymous","qualified":"bindings.BindingFixture::anonymous","startUtf16":1093,"endUtf16":1257,"startLine":21,"endLine":26}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1142:1250:class:","kind":"class","file":"BindingFixture.java","name":"","qualified":"","startUtf16":1142,"endUtf16":1250,"startLine":22,"endLine":25}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1156:1165:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"::left","startUtf16":1156,"endUtf16":1165,"startLine":23,"endLine":23}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1178:1204:method:get","kind":"method","file":"BindingFixture.java","name":"get","qualified":"::get","startUtf16":1178,"endUtf16":1204,"startLine":24,"endLine":24}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":1197,"expressionEndUtf16":1201,"tokenStartUtf16":1197,"tokenEndUtf16":1201,"anchorStatus":"exact","line":24,"targetKind":"field","targetQualified":"::left","targetOrigin":"source","target":{"id":"BindingFixture.java:1156:1165:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"::left","startUtf16":1156,"endUtf16":1165,"startLine":23,"endLine":23},"owner":{"id":"BindingFixture.java:1178:1204:method:get","kind":"method","file":"BindingFixture.java","name":"get","qualified":"::get","startUtf16":1178,"endUtf16":1204,"startLine":24,"endLine":24}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1262:1440:method:local","kind":"method","file":"BindingFixture.java","name":"local","qualified":"bindings.BindingFixture::local","startUtf16":1262,"endUtf16":1440,"startLine":27,"endLine":33}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1284:1400:class:Local","kind":"class","file":"BindingFixture.java","name":"Local","qualified":"Local","startUtf16":1284,"endUtf16":1400,"startLine":28,"endLine":31}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1310:1319:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"Local::left","startUtf16":1310,"endUtf16":1319,"startLine":29,"endLine":29}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1332:1358:method:get","kind":"method","file":"BindingFixture.java","name":"get","qualified":"Local::get","startUtf16":1332,"endUtf16":1358,"startLine":30,"endLine":30}} +{"type":"fieldReference","file":"BindingFixture.java","name":"left","expressionStartUtf16":1351,"expressionEndUtf16":1355,"tokenStartUtf16":1351,"tokenEndUtf16":1355,"anchorStatus":"exact","line":30,"targetKind":"field","targetQualified":"Local::left","targetOrigin":"source","target":{"id":"BindingFixture.java:1310:1319:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"Local::left","startUtf16":1310,"endUtf16":1319,"startLine":29,"endLine":29},"owner":{"id":"BindingFixture.java:1332:1358:method:get","kind":"method","file":"BindingFixture.java","name":"get","qualified":"Local::get","startUtf16":1332,"endUtf16":1358,"startLine":30,"endLine":30}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1445:1464:enum:Token","kind":"enum","file":"BindingFixture.java","name":"Token","qualified":"bindings.BindingFixture.Token","startUtf16":1445,"endUtf16":1464,"startLine":34,"endLine":34}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1458:1459:enum_constant:A","kind":"enum_constant","file":"BindingFixture.java","name":"A","qualified":"bindings.BindingFixture.Token::A","startUtf16":1458,"endUtf16":1459,"startLine":34,"endLine":34}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1461:1462:enum_constant:B","kind":"enum_constant","file":"BindingFixture.java","name":"B","qualified":"bindings.BindingFixture.Token::B","startUtf16":1461,"endUtf16":1462,"startLine":34,"endLine":34}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1469:1502:method:token","kind":"method","file":"BindingFixture.java","name":"token","qualified":"bindings.BindingFixture::token","startUtf16":1469,"endUtf16":1502,"startLine":35,"endLine":35}} +{"type":"fieldReference","file":"BindingFixture.java","name":"A","expressionStartUtf16":1492,"expressionEndUtf16":1499,"tokenStartUtf16":1498,"tokenEndUtf16":1499,"anchorStatus":"exact","line":35,"targetKind":"enum_constant","targetQualified":"bindings.BindingFixture.Token::A","targetOrigin":"source","target":{"id":"BindingFixture.java:1458:1459:enum_constant:A","kind":"enum_constant","file":"BindingFixture.java","name":"A","qualified":"bindings.BindingFixture.Token::A","startUtf16":1458,"endUtf16":1459,"startLine":34,"endLine":34},"owner":{"id":"BindingFixture.java:1469:1502:method:token","kind":"method","file":"BindingFixture.java","name":"token","qualified":"bindings.BindingFixture::token","startUtf16":1469,"endUtf16":1502,"startLine":35,"endLine":35}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1533:1628:method:intrinsic","kind":"method","file":"BindingFixture.java","name":"intrinsic","qualified":"bindings.BindingFixture::intrinsic","startUtf16":1533,"endUtf16":1628,"startLine":36,"endLine":36}} +{"type":"fieldReference","file":"BindingFixture.java","name":"length","expressionStartUtf16":1570,"expressionEndUtf16":1583,"tokenStartUtf16":1577,"tokenEndUtf16":1583,"anchorStatus":"exact","line":36,"targetKind":"field","targetQualified":"Array::length","targetOrigin":"array-length","target":null,"owner":{"id":"BindingFixture.java:1533:1628:method:intrinsic","kind":"method","file":"BindingFixture.java","name":"intrinsic","qualified":"bindings.BindingFixture::intrinsic","startUtf16":1533,"endUtf16":1628,"startLine":36,"endLine":36}} +{"type":"fieldReference","file":"BindingFixture.java","name":"class","expressionStartUtf16":1586,"expressionEndUtf16":1606,"tokenStartUtf16":1601,"tokenEndUtf16":1606,"anchorStatus":"exact","line":36,"targetKind":"field","targetQualified":"bindings.BindingFixture::class","targetOrigin":"class-literal","target":null,"owner":{"id":"BindingFixture.java:1533:1628:method:intrinsic","kind":"method","file":"BindingFixture.java","name":"intrinsic","qualified":"bindings.BindingFixture::intrinsic","startUtf16":1533,"endUtf16":1628,"startLine":36,"endLine":36}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1649:1673:class:Base","kind":"class","file":"BindingFixture.java","name":"Base","qualified":"bindings.Base","startUtf16":1649,"endUtf16":1673,"startLine":38,"endLine":38}} +{"type":"declaration","declaration":{"id":"BindingFixture.java:1662:1671:field:left","kind":"field","file":"BindingFixture.java","name":"left","qualified":"bindings.Base::left","startUtf16":1662,"endUtf16":1671,"startLine":38,"endLine":38}} +{"type":"complete","declarations":31,"fieldReferences":20,"unboundExpressions":0} diff --git a/benchmarks/agent_query/fixtures/java_field_bindings/scope-manifest.json b/benchmarks/agent_query/fixtures/java_field_bindings/scope-manifest.json new file mode 100644 index 000000000..12f9b1405 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_field_bindings/scope-manifest.json @@ -0,0 +1,8 @@ +{ + "files": { + "ScopeFixture.java": "bfc306296dbc95809b0d0171ed770785ce442ba6253dd98ecbbf37cff9730335" + }, + "captureSha256": "f9add7ff7e6b7158f6a0afdc8be51c439417952b7f4a26dad8eedd047484b1c6", + "toolSha256": "0616d29730f77fc33a313e5c3280d6905c5435433f8e52ac4250ce2a8124e9d6", + "compilerRuntime": "17.0.8.1+8-LTS" +} diff --git a/benchmarks/agent_query/fixtures/java_field_bindings/scope.jsonl b/benchmarks/agent_query/fixtures/java_field_bindings/scope.jsonl new file mode 100644 index 000000000..1b97a46b6 --- /dev/null +++ b/benchmarks/agent_query/fixtures/java_field_bindings/scope.jsonl @@ -0,0 +1,105 @@ +{"type":"header","schema":"compass.javac-field-bindings/1","files":1,"sourceBytes":5634,"release":"17","positionEncoding":"UTF-16 code units","compilerRuntime":"17.0.8.1+8-LTS"} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:182:5362:class:ScopeFixture","kind":"class","file":"ScopeFixture.java","name":"ScopeFixture","qualified":"audit.ScopeFixture","startUtf16":182,"endUtf16":5362,"startLine":7,"endLine":154}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:263:273:field:slot","kind":"field","file":"ScopeFixture.java","name":"slot","qualified":"audit.ScopeFixture::slot","startUtf16":263,"endUtf16":273,"startLine":9,"endLine":9}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:298:311:field:items","kind":"field","file":"ScopeFixture.java","name":"items","qualified":"audit.ScopeFixture::items","startUtf16":298,"endUtf16":311,"startLine":10,"endLine":10}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:337:362:field:failure","kind":"field","file":"ScopeFixture.java","name":"failure","qualified":"audit.ScopeFixture::failure","startUtf16":337,"endUtf16":362,"startLine":11,"endLine":11}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:390:405:field:resource","kind":"field","file":"ScopeFixture.java","name":"resource","qualified":"audit.ScopeFixture::resource","startUtf16":390,"endUtf16":405,"startLine":12,"endLine":12}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:434:449:field:pattern","kind":"field","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":434,"endUtf16":449,"startLine":13,"endLine":13}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:477:500:field:staticValue","kind":"field","file":"ScopeFixture.java","name":"staticValue","qualified":"audit.ScopeFixture::staticValue","startUtf16":477,"endUtf16":500,"startLine":14,"endLine":14}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:528:620:constructor:","kind":"constructor","file":"ScopeFixture.java","name":"","qualified":"audit.ScopeFixture::","startUtf16":528,"endUtf16":620,"startLine":16,"endLine":18}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":562,"expressionEndUtf16":572,"tokenStartUtf16":567,"tokenEndUtf16":572,"anchorStatus":"exact","line":17,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:528:620:constructor:","kind":"constructor","file":"ScopeFixture.java","name":"","qualified":"audit.ScopeFixture::","startUtf16":528,"endUtf16":620,"startLine":16,"endLine":18}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:625:709:method:explicit","kind":"method","file":"ScopeFixture.java","name":"explicit","qualified":"audit.ScopeFixture::explicit","startUtf16":625,"endUtf16":709,"startLine":19,"endLine":21}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":657,"expressionEndUtf16":667,"tokenStartUtf16":662,"tokenEndUtf16":667,"anchorStatus":"exact","line":20,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:625:709:method:explicit","kind":"method","file":"ScopeFixture.java","name":"explicit","qualified":"audit.ScopeFixture::explicit","startUtf16":625,"endUtf16":709,"startLine":19,"endLine":21}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:714:793:method:implicit","kind":"method","file":"ScopeFixture.java","name":"implicit","qualified":"audit.ScopeFixture::implicit","startUtf16":714,"endUtf16":793,"startLine":22,"endLine":24}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":746,"expressionEndUtf16":751,"tokenStartUtf16":746,"tokenEndUtf16":751,"anchorStatus":"exact","line":23,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:714:793:method:implicit","kind":"method","file":"ScopeFixture.java","name":"implicit","qualified":"audit.ScopeFixture::implicit","startUtf16":714,"endUtf16":793,"startLine":22,"endLine":24}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:798:897:method:repeated","kind":"method","file":"ScopeFixture.java","name":"repeated","qualified":"audit.ScopeFixture::repeated","startUtf16":798,"endUtf16":897,"startLine":25,"endLine":27}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":830,"expressionEndUtf16":835,"tokenStartUtf16":830,"tokenEndUtf16":835,"anchorStatus":"exact","line":26,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:798:897:method:repeated","kind":"method","file":"ScopeFixture.java","name":"repeated","qualified":"audit.ScopeFixture::repeated","startUtf16":798,"endUtf16":897,"startLine":25,"endLine":27}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":838,"expressionEndUtf16":848,"tokenStartUtf16":843,"tokenEndUtf16":848,"anchorStatus":"exact","line":26,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:798:897:method:repeated","kind":"method","file":"ScopeFixture.java","name":"repeated","qualified":"audit.ScopeFixture::repeated","startUtf16":798,"endUtf16":897,"startLine":25,"endLine":27}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:902:984:method:parameter","kind":"method","file":"ScopeFixture.java","name":"parameter","qualified":"audit.ScopeFixture::parameter","startUtf16":902,"endUtf16":984,"startLine":28,"endLine":30}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:989:1214:method:block","kind":"method","file":"ScopeFixture.java","name":"block","qualified":"audit.ScopeFixture::block","startUtf16":989,"endUtf16":1214,"startLine":31,"endLine":38}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1017,"expressionEndUtf16":1022,"tokenStartUtf16":1017,"tokenEndUtf16":1022,"anchorStatus":"exact","line":32,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:989:1214:method:block","kind":"method","file":"ScopeFixture.java","name":"block","qualified":"audit.ScopeFixture::block","startUtf16":989,"endUtf16":1214,"startLine":31,"endLine":38}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1168,"expressionEndUtf16":1173,"tokenStartUtf16":1168,"tokenEndUtf16":1173,"anchorStatus":"exact","line":37,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:989:1214:method:block","kind":"method","file":"ScopeFixture.java","name":"block","qualified":"audit.ScopeFixture::block","startUtf16":989,"endUtf16":1214,"startLine":31,"endLine":38}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:1219:1367:method:beforeLocal","kind":"method","file":"ScopeFixture.java","name":"beforeLocal","qualified":"audit.ScopeFixture::beforeLocal","startUtf16":1219,"endUtf16":1367,"startLine":39,"endLine":43}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1252,"expressionEndUtf16":1257,"tokenStartUtf16":1252,"tokenEndUtf16":1257,"anchorStatus":"exact","line":40,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:1219:1367:method:beforeLocal","kind":"method","file":"ScopeFixture.java","name":"beforeLocal","qualified":"audit.ScopeFixture::beforeLocal","startUtf16":1219,"endUtf16":1367,"startLine":39,"endLine":43}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:1372:1550:method:loop","kind":"method","file":"ScopeFixture.java","name":"loop","qualified":"audit.ScopeFixture::loop","startUtf16":1372,"endUtf16":1550,"startLine":44,"endLine":49}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1506,"expressionEndUtf16":1511,"tokenStartUtf16":1506,"tokenEndUtf16":1511,"anchorStatus":"exact","line":48,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:1372:1550:method:loop","kind":"method","file":"ScopeFixture.java","name":"loop","qualified":"audit.ScopeFixture::loop","startUtf16":1372,"endUtf16":1550,"startLine":44,"endLine":49}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:1555:1800:method:enhanced","kind":"method","file":"ScopeFixture.java","name":"enhanced","qualified":"audit.ScopeFixture::enhanced","startUtf16":1555,"endUtf16":1800,"startLine":50,"endLine":55}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"items","expressionStartUtf16":1597,"expressionEndUtf16":1602,"tokenStartUtf16":1597,"tokenEndUtf16":1602,"anchorStatus":"exact","line":51,"targetKind":"field","targetQualified":"audit.ScopeFixture::items","targetOrigin":"source","target":{"id":"ScopeFixture.java:298:311:field:items","kind":"field","file":"ScopeFixture.java","name":"items","qualified":"audit.ScopeFixture::items","startUtf16":298,"endUtf16":311,"startLine":10,"endLine":10},"owner":{"id":"ScopeFixture.java:1555:1800:method:enhanced","kind":"method","file":"ScopeFixture.java","name":"enhanced","qualified":"audit.ScopeFixture::enhanced","startUtf16":1555,"endUtf16":1800,"startLine":50,"endLine":55}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1662,"expressionEndUtf16":1672,"tokenStartUtf16":1667,"tokenEndUtf16":1672,"anchorStatus":"exact","line":52,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:1555:1800:method:enhanced","kind":"method","file":"ScopeFixture.java","name":"enhanced","qualified":"audit.ScopeFixture::enhanced","startUtf16":1555,"endUtf16":1800,"startLine":50,"endLine":55}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1737,"expressionEndUtf16":1747,"tokenStartUtf16":1742,"tokenEndUtf16":1747,"anchorStatus":"exact","line":54,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:1555:1800:method:enhanced","kind":"method","file":"ScopeFixture.java","name":"enhanced","qualified":"audit.ScopeFixture::enhanced","startUtf16":1555,"endUtf16":1800,"startLine":50,"endLine":55}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"slot","expressionStartUtf16":1737,"expressionEndUtf16":1741,"tokenStartUtf16":1737,"tokenEndUtf16":1741,"anchorStatus":"exact","line":54,"targetKind":"field","targetQualified":"audit.ScopeFixture::slot","targetOrigin":"source","target":{"id":"ScopeFixture.java:263:273:field:slot","kind":"field","file":"ScopeFixture.java","name":"slot","qualified":"audit.ScopeFixture::slot","startUtf16":263,"endUtf16":273,"startLine":9,"endLine":9},"owner":{"id":"ScopeFixture.java:1555:1800:method:enhanced","kind":"method","file":"ScopeFixture.java","name":"enhanced","qualified":"audit.ScopeFixture::enhanced","startUtf16":1555,"endUtf16":1800,"startLine":50,"endLine":55}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:1805:2107:method:lambdas","kind":"method","file":"ScopeFixture.java","name":"lambdas","qualified":"audit.ScopeFixture::lambdas","startUtf16":1805,"endUtf16":2107,"startLine":56,"endLine":61}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":1926,"expressionEndUtf16":1936,"tokenStartUtf16":1931,"tokenEndUtf16":1936,"anchorStatus":"exact","line":58,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:1805:2107:method:lambdas","kind":"method","file":"ScopeFixture.java","name":"lambdas","qualified":"audit.ScopeFixture::lambdas","startUtf16":1805,"endUtf16":2107,"startLine":56,"endLine":61}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2001,"expressionEndUtf16":2006,"tokenStartUtf16":2001,"tokenEndUtf16":2006,"anchorStatus":"exact","line":59,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:1805:2107:method:lambdas","kind":"method","file":"ScopeFixture.java","name":"lambdas","qualified":"audit.ScopeFixture::lambdas","startUtf16":1805,"endUtf16":2107,"startLine":56,"endLine":61}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:2112:2374:method:catches","kind":"method","file":"ScopeFixture.java","name":"catches","qualified":"audit.ScopeFixture::catches","startUtf16":2112,"endUtf16":2374,"startLine":62,"endLine":68}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"failure","expressionStartUtf16":2308,"expressionEndUtf16":2315,"tokenStartUtf16":2308,"tokenEndUtf16":2315,"anchorStatus":"exact","line":67,"targetKind":"field","targetQualified":"audit.ScopeFixture::failure","targetOrigin":"source","target":{"id":"ScopeFixture.java:337:362:field:failure","kind":"field","file":"ScopeFixture.java","name":"failure","qualified":"audit.ScopeFixture::failure","startUtf16":337,"endUtf16":362,"startLine":11,"endLine":11},"owner":{"id":"ScopeFixture.java:2112:2374:method:catches","kind":"method","file":"ScopeFixture.java","name":"catches","qualified":"audit.ScopeFixture::catches","startUtf16":2112,"endUtf16":2374,"startLine":62,"endLine":68}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2460,"expressionEndUtf16":2474,"tokenStartUtf16":2469,"tokenEndUtf16":2474,"anchorStatus":"exact","line":71,"targetKind":"field","targetQualified":"audit.Token::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5573:5583:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Token::value","startUtf16":5573,"endUtf16":5583,"startLine":165,"endLine":165},"owner":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2571,"expressionEndUtf16":2585,"tokenStartUtf16":2580,"tokenEndUtf16":2585,"anchorStatus":"exact","line":73,"targetKind":"field","targetQualified":"audit.Token::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5573:5583:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Token::value","startUtf16":5573,"endUtf16":5583,"startLine":165,"endLine":165},"owner":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"resource","expressionStartUtf16":2571,"expressionEndUtf16":2579,"tokenStartUtf16":2571,"tokenEndUtf16":2579,"anchorStatus":"exact","line":73,"targetKind":"field","targetQualified":"audit.ScopeFixture::resource","targetOrigin":"source","target":{"id":"ScopeFixture.java:390:405:field:resource","kind":"field","file":"ScopeFixture.java","name":"resource","qualified":"audit.ScopeFixture::resource","startUtf16":390,"endUtf16":405,"startLine":12,"endLine":12},"owner":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2662,"expressionEndUtf16":2676,"tokenStartUtf16":2671,"tokenEndUtf16":2676,"anchorStatus":"exact","line":75,"targetKind":"field","targetQualified":"audit.Token::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5573:5583:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Token::value","startUtf16":5573,"endUtf16":5583,"startLine":165,"endLine":165},"owner":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"resource","expressionStartUtf16":2662,"expressionEndUtf16":2670,"tokenStartUtf16":2662,"tokenEndUtf16":2670,"anchorStatus":"exact","line":75,"targetKind":"field","targetQualified":"audit.ScopeFixture::resource","targetOrigin":"source","target":{"id":"ScopeFixture.java:390:405:field:resource","kind":"field","file":"ScopeFixture.java","name":"resource","qualified":"audit.ScopeFixture::resource","startUtf16":390,"endUtf16":405,"startLine":12,"endLine":12},"owner":{"id":"ScopeFixture.java:2379:2735:method:resources","kind":"method","file":"ScopeFixture.java","name":"resources","qualified":"audit.ScopeFixture::resources","startUtf16":2379,"endUtf16":2735,"startLine":69,"endLine":76}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:2740:2961:method:pattern","kind":"method","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":2740,"endUtf16":2961,"startLine":77,"endLine":82}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2829,"expressionEndUtf16":2839,"tokenStartUtf16":2834,"tokenEndUtf16":2839,"anchorStatus":"exact","line":79,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:2740:2961:method:pattern","kind":"method","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":2740,"endUtf16":2961,"startLine":77,"endLine":82}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":2899,"expressionEndUtf16":2909,"tokenStartUtf16":2904,"tokenEndUtf16":2909,"anchorStatus":"exact","line":81,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:2740:2961:method:pattern","kind":"method","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":2740,"endUtf16":2961,"startLine":77,"endLine":82}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"slot","expressionStartUtf16":2899,"expressionEndUtf16":2903,"tokenStartUtf16":2899,"tokenEndUtf16":2903,"anchorStatus":"exact","line":81,"targetKind":"field","targetQualified":"audit.ScopeFixture::slot","targetOrigin":"source","target":{"id":"ScopeFixture.java:263:273:field:slot","kind":"field","file":"ScopeFixture.java","name":"slot","qualified":"audit.ScopeFixture::slot","startUtf16":263,"endUtf16":273,"startLine":9,"endLine":9},"owner":{"id":"ScopeFixture.java:2740:2961:method:pattern","kind":"method","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":2740,"endUtf16":2961,"startLine":77,"endLine":82}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:2966:3146:method:negatedPattern","kind":"method","file":"ScopeFixture.java","name":"negatedPattern","qualified":"audit.ScopeFixture::negatedPattern","startUtf16":2966,"endUtf16":3146,"startLine":83,"endLine":86}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"pattern","expressionStartUtf16":3003,"expressionEndUtf16":3010,"tokenStartUtf16":3003,"tokenEndUtf16":3010,"anchorStatus":"exact","line":84,"targetKind":"field","targetQualified":"audit.ScopeFixture::pattern","targetOrigin":"source","target":{"id":"ScopeFixture.java:434:449:field:pattern","kind":"field","file":"ScopeFixture.java","name":"pattern","qualified":"audit.ScopeFixture::pattern","startUtf16":434,"endUtf16":449,"startLine":13,"endLine":13},"owner":{"id":"ScopeFixture.java:2966:3146:method:negatedPattern","kind":"method","file":"ScopeFixture.java","name":"negatedPattern","qualified":"audit.ScopeFixture::negatedPattern","startUtf16":2966,"endUtf16":3146,"startLine":83,"endLine":86}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3096,"expressionEndUtf16":3106,"tokenStartUtf16":3101,"tokenEndUtf16":3106,"anchorStatus":"exact","line":85,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:2966:3146:method:negatedPattern","kind":"method","file":"ScopeFixture.java","name":"negatedPattern","qualified":"audit.ScopeFixture::negatedPattern","startUtf16":2966,"endUtf16":3146,"startLine":83,"endLine":86}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3151:3242:method:typed","kind":"method","file":"ScopeFixture.java","name":"typed","qualified":"audit.ScopeFixture::typed","startUtf16":3151,"endUtf16":3242,"startLine":87,"endLine":89}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3189,"expressionEndUtf16":3199,"tokenStartUtf16":3194,"tokenEndUtf16":3199,"anchorStatus":"exact","line":88,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3151:3242:method:typed","kind":"method","file":"ScopeFixture.java","name":"typed","qualified":"audit.ScopeFixture::typed","startUtf16":3151,"endUtf16":3242,"startLine":87,"endLine":89}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3247:3346:method:cast","kind":"method","file":"ScopeFixture.java","name":"cast","qualified":"audit.ScopeFixture::cast","startUtf16":3247,"endUtf16":3346,"startLine":90,"endLine":92}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3286,"expressionEndUtf16":3305,"tokenStartUtf16":3300,"tokenEndUtf16":3305,"anchorStatus":"exact","line":91,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3247:3346:method:cast","kind":"method","file":"ScopeFixture.java","name":"cast","qualified":"audit.ScopeFixture::cast","startUtf16":3247,"endUtf16":3346,"startLine":90,"endLine":92}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3351:3440:method:chain","kind":"method","file":"ScopeFixture.java","name":"chain","qualified":"audit.ScopeFixture::chain","startUtf16":3351,"endUtf16":3440,"startLine":93,"endLine":95}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3380,"expressionEndUtf16":3390,"tokenStartUtf16":3385,"tokenEndUtf16":3390,"anchorStatus":"exact","line":94,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3351:3440:method:chain","kind":"method","file":"ScopeFixture.java","name":"chain","qualified":"audit.ScopeFixture::chain","startUtf16":3351,"endUtf16":3440,"startLine":93,"endLine":95}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"slot","expressionStartUtf16":3380,"expressionEndUtf16":3384,"tokenStartUtf16":3380,"tokenEndUtf16":3384,"anchorStatus":"exact","line":94,"targetKind":"field","targetQualified":"audit.ScopeFixture::slot","targetOrigin":"source","target":{"id":"ScopeFixture.java:263:273:field:slot","kind":"field","file":"ScopeFixture.java","name":"slot","qualified":"audit.ScopeFixture::slot","startUtf16":263,"endUtf16":273,"startLine":9,"endLine":9},"owner":{"id":"ScopeFixture.java:3351:3440:method:chain","kind":"method","file":"ScopeFixture.java","name":"chain","qualified":"audit.ScopeFixture::chain","startUtf16":3351,"endUtf16":3440,"startLine":93,"endLine":95}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3445:3544:method:indexed","kind":"method","file":"ScopeFixture.java","name":"indexed","qualified":"audit.ScopeFixture::indexed","startUtf16":3445,"endUtf16":3544,"startLine":96,"endLine":98}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3476,"expressionEndUtf16":3490,"tokenStartUtf16":3485,"tokenEndUtf16":3490,"anchorStatus":"exact","line":97,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3445:3544:method:indexed","kind":"method","file":"ScopeFixture.java","name":"indexed","qualified":"audit.ScopeFixture::indexed","startUtf16":3445,"endUtf16":3544,"startLine":96,"endLine":98}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"items","expressionStartUtf16":3476,"expressionEndUtf16":3481,"tokenStartUtf16":3476,"tokenEndUtf16":3481,"anchorStatus":"exact","line":97,"targetKind":"field","targetQualified":"audit.ScopeFixture::items","targetOrigin":"source","target":{"id":"ScopeFixture.java:298:311:field:items","kind":"field","file":"ScopeFixture.java","name":"items","qualified":"audit.ScopeFixture::items","startUtf16":298,"endUtf16":311,"startLine":10,"endLine":10},"owner":{"id":"ScopeFixture.java:3445:3544:method:indexed","kind":"method","file":"ScopeFixture.java","name":"indexed","qualified":"audit.ScopeFixture::indexed","startUtf16":3445,"endUtf16":3544,"startLine":96,"endLine":98}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3549:3629:method:parent","kind":"method","file":"ScopeFixture.java","name":"parent","qualified":"audit.ScopeFixture::parent","startUtf16":3549,"endUtf16":3629,"startLine":99,"endLine":101}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3579,"expressionEndUtf16":3590,"tokenStartUtf16":3585,"tokenEndUtf16":3590,"anchorStatus":"exact","line":100,"targetKind":"field","targetQualified":"audit.Base::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5380:5390:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Base::value","startUtf16":5380,"endUtf16":5390,"startLine":156,"endLine":156},"owner":{"id":"ScopeFixture.java:3549:3629:method:parent","kind":"method","file":"ScopeFixture.java","name":"parent","qualified":"audit.ScopeFixture::parent","startUtf16":3549,"endUtf16":3629,"startLine":99,"endLine":101}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3634:3750:method:staticBinding","kind":"method","file":"ScopeFixture.java","name":"staticBinding","qualified":"audit.ScopeFixture::staticBinding","startUtf16":3634,"endUtf16":3750,"startLine":102,"endLine":104}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3695,"expressionEndUtf16":3705,"tokenStartUtf16":3700,"tokenEndUtf16":3705,"anchorStatus":"exact","line":103,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3634:3750:method:staticBinding","kind":"method","file":"ScopeFixture.java","name":"staticBinding","qualified":"audit.ScopeFixture::staticBinding","startUtf16":3634,"endUtf16":3750,"startLine":102,"endLine":104}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3755:3852:method:subtype","kind":"method","file":"ScopeFixture.java","name":"subtype","qualified":"audit.ScopeFixture::subtype","startUtf16":3755,"endUtf16":3852,"startLine":105,"endLine":107}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3797,"expressionEndUtf16":3807,"tokenStartUtf16":3802,"tokenEndUtf16":3807,"anchorStatus":"exact","line":106,"targetKind":"field","targetQualified":"audit.Shadow::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5495:5505:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Shadow::value","startUtf16":5495,"endUtf16":5505,"startLine":162,"endLine":162},"owner":{"id":"ScopeFixture.java:3755:3852:method:subtype","kind":"method","file":"ScopeFixture.java","name":"subtype","qualified":"audit.ScopeFixture::subtype","startUtf16":3755,"endUtf16":3852,"startLine":105,"endLine":107}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3857:3962:method:generic","kind":"method","file":"ScopeFixture.java","name":"generic","qualified":"audit.ScopeFixture::generic","startUtf16":3857,"endUtf16":3962,"startLine":108,"endLine":110}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":3911,"expressionEndUtf16":3921,"tokenStartUtf16":3916,"tokenEndUtf16":3921,"anchorStatus":"exact","line":109,"targetKind":"field","targetQualified":"audit.Cell::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159},"owner":{"id":"ScopeFixture.java:3857:3962:method:generic","kind":"method","file":"ScopeFixture.java","name":"generic","qualified":"audit.ScopeFixture::generic","startUtf16":3857,"endUtf16":3962,"startLine":108,"endLine":110}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:3967:4099:method:statics","kind":"method","file":"ScopeFixture.java","name":"statics","qualified":"audit.ScopeFixture::statics","startUtf16":3967,"endUtf16":4099,"startLine":111,"endLine":113}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"staticValue","expressionStartUtf16":4005,"expressionEndUtf16":4016,"tokenStartUtf16":4005,"tokenEndUtf16":4016,"anchorStatus":"exact","line":112,"targetKind":"field","targetQualified":"audit.ScopeFixture::staticValue","targetOrigin":"source","target":{"id":"ScopeFixture.java:477:500:field:staticValue","kind":"field","file":"ScopeFixture.java","name":"staticValue","qualified":"audit.ScopeFixture::staticValue","startUtf16":477,"endUtf16":500,"startLine":14,"endLine":14},"owner":{"id":"ScopeFixture.java:3967:4099:method:statics","kind":"method","file":"ScopeFixture.java","name":"statics","qualified":"audit.ScopeFixture::statics","startUtf16":3967,"endUtf16":4099,"startLine":111,"endLine":113}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"staticValue","expressionStartUtf16":4019,"expressionEndUtf16":4043,"tokenStartUtf16":4032,"tokenEndUtf16":4043,"anchorStatus":"exact","line":112,"targetKind":"field","targetQualified":"audit.ScopeFixture::staticValue","targetOrigin":"source","target":{"id":"ScopeFixture.java:477:500:field:staticValue","kind":"field","file":"ScopeFixture.java","name":"staticValue","qualified":"audit.ScopeFixture::staticValue","startUtf16":477,"endUtf16":500,"startLine":14,"endLine":14},"owner":{"id":"ScopeFixture.java:3967:4099:method:statics","kind":"method","file":"ScopeFixture.java","name":"statics","qualified":"audit.ScopeFixture::statics","startUtf16":3967,"endUtf16":4099,"startLine":111,"endLine":113}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4104:4129:method:value","kind":"method","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":4104,"endUtf16":4129,"startLine":114,"endLine":114}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4134:4214:method:methodSelector","kind":"method","file":"ScopeFixture.java","name":"methodSelector","qualified":"audit.ScopeFixture::methodSelector","startUtf16":4134,"endUtf16":4214,"startLine":115,"endLine":117}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4219:4379:method:declarators","kind":"method","file":"ScopeFixture.java","name":"declarators","qualified":"audit.ScopeFixture::declarators","startUtf16":4219,"endUtf16":4379,"startLine":118,"endLine":121}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4260,"expressionEndUtf16":4265,"tokenStartUtf16":4260,"tokenEndUtf16":4265,"anchorStatus":"exact","line":119,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:4219:4379:method:declarators","kind":"method","file":"ScopeFixture.java","name":"declarators","qualified":"audit.ScopeFixture::declarators","startUtf16":4219,"endUtf16":4379,"startLine":118,"endLine":121}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4384:4642:class:Inner","kind":"class","file":"ScopeFixture.java","name":"Inner","qualified":"audit.ScopeFixture.Inner","startUtf16":4384,"endUtf16":4642,"startLine":122,"endLine":130}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4406:4416:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture.Inner::value","startUtf16":4406,"endUtf16":4416,"startLine":123,"endLine":123}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4446:4524:method:own","kind":"method","file":"ScopeFixture.java","name":"own","qualified":"audit.ScopeFixture.Inner::own","startUtf16":4446,"endUtf16":4524,"startLine":124,"endLine":126}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4477,"expressionEndUtf16":4482,"tokenStartUtf16":4477,"tokenEndUtf16":4482,"anchorStatus":"exact","line":125,"targetKind":"field","targetQualified":"audit.ScopeFixture.Inner::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:4406:4416:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture.Inner::value","startUtf16":4406,"endUtf16":4416,"startLine":123,"endLine":123},"owner":{"id":"ScopeFixture.java:4446:4524:method:own","kind":"method","file":"ScopeFixture.java","name":"own","qualified":"audit.ScopeFixture.Inner::own","startUtf16":4446,"endUtf16":4524,"startLine":124,"endLine":126}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4533:4636:method:outer","kind":"method","file":"ScopeFixture.java","name":"outer","qualified":"audit.ScopeFixture.Inner::outer","startUtf16":4533,"endUtf16":4636,"startLine":127,"endLine":129}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4566,"expressionEndUtf16":4589,"tokenStartUtf16":4584,"tokenEndUtf16":4589,"anchorStatus":"exact","line":128,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:4533:4636:method:outer","kind":"method","file":"ScopeFixture.java","name":"outer","qualified":"audit.ScopeFixture.Inner::outer","startUtf16":4533,"endUtf16":4636,"startLine":127,"endLine":129}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4647:4787:class:Inherited","kind":"class","file":"ScopeFixture.java","name":"Inherited","qualified":"audit.ScopeFixture.Inherited","startUtf16":4647,"endUtf16":4787,"startLine":131,"endLine":135}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4686:4781:method:inherited","kind":"method","file":"ScopeFixture.java","name":"inherited","qualified":"audit.ScopeFixture.Inherited::inherited","startUtf16":4686,"endUtf16":4781,"startLine":132,"endLine":134}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4723,"expressionEndUtf16":4728,"tokenStartUtf16":4723,"tokenEndUtf16":4728,"anchorStatus":"exact","line":133,"targetKind":"field","targetQualified":"audit.Base::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5380:5390:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Base::value","startUtf16":5380,"endUtf16":5390,"startLine":156,"endLine":156},"owner":{"id":"ScopeFixture.java:4686:4781:method:inherited","kind":"method","file":"ScopeFixture.java","name":"inherited","qualified":"audit.ScopeFixture.Inherited::inherited","startUtf16":4686,"endUtf16":4781,"startLine":132,"endLine":134}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4792:5080:method:anonymous","kind":"method","file":"ScopeFixture.java","name":"anonymous","qualified":"audit.ScopeFixture::anonymous","startUtf16":4792,"endUtf16":5080,"startLine":136,"endLine":143}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4851:5073:class:","kind":"class","file":"ScopeFixture.java","name":"","qualified":"","startUtf16":4851,"endUtf16":5073,"startLine":137,"endLine":142}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4865:4875:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"::value","startUtf16":4865,"endUtf16":4875,"startLine":138,"endLine":138}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:4913:5063:method:getAsInt","kind":"method","file":"ScopeFixture.java","name":"getAsInt","qualified":"::getAsInt","startUtf16":4913,"endUtf16":5063,"startLine":139,"endLine":141}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4960,"expressionEndUtf16":4970,"tokenStartUtf16":4965,"tokenEndUtf16":4970,"anchorStatus":"exact","line":140,"targetKind":"field","targetQualified":"::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:4865:4875:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"::value","startUtf16":4865,"endUtf16":4875,"startLine":138,"endLine":138},"owner":{"id":"ScopeFixture.java:4913:5063:method:getAsInt","kind":"method","file":"ScopeFixture.java","name":"getAsInt","qualified":"::getAsInt","startUtf16":4913,"endUtf16":5063,"startLine":139,"endLine":141}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":4973,"expressionEndUtf16":4996,"tokenStartUtf16":4991,"tokenEndUtf16":4996,"anchorStatus":"exact","line":140,"targetKind":"field","targetQualified":"audit.ScopeFixture::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:227:237:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.ScopeFixture::value","startUtf16":227,"endUtf16":237,"startLine":8,"endLine":8},"owner":{"id":"ScopeFixture.java:4913:5063:method:getAsInt","kind":"method","file":"ScopeFixture.java","name":"getAsInt","qualified":"::getAsInt","startUtf16":4913,"endUtf16":5063,"startLine":139,"endLine":141}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5085:5325:method:local","kind":"method","file":"ScopeFixture.java","name":"local","qualified":"audit.ScopeFixture::local","startUtf16":5085,"endUtf16":5325,"startLine":144,"endLine":152}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5107:5285:class:Local","kind":"class","file":"ScopeFixture.java","name":"Local","qualified":"Local","startUtf16":5107,"endUtf16":5285,"startLine":145,"endLine":150}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5133:5143:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"Local::value","startUtf16":5133,"endUtf16":5143,"startLine":146,"endLine":146}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5177:5275:method:get","kind":"method","file":"ScopeFixture.java","name":"get","qualified":"Local::get","startUtf16":5177,"endUtf16":5275,"startLine":147,"endLine":149}} +{"type":"fieldReference","file":"ScopeFixture.java","name":"value","expressionStartUtf16":5212,"expressionEndUtf16":5222,"tokenStartUtf16":5217,"tokenEndUtf16":5222,"anchorStatus":"exact","line":148,"targetKind":"field","targetQualified":"Local::value","targetOrigin":"source","target":{"id":"ScopeFixture.java:5133:5143:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"Local::value","startUtf16":5133,"endUtf16":5143,"startLine":146,"endLine":146},"owner":{"id":"ScopeFixture.java:5177:5275:method:get","kind":"method","file":"ScopeFixture.java","name":"get","qualified":"Local::get","startUtf16":5177,"endUtf16":5275,"startLine":147,"endLine":149}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5330:5360:method:sink","kind":"method","file":"ScopeFixture.java","name":"sink","qualified":"audit.ScopeFixture::sink","startUtf16":5330,"endUtf16":5360,"startLine":153,"endLine":153}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5363:5412:class:Base","kind":"class","file":"ScopeFixture.java","name":"Base","qualified":"audit.Base","startUtf16":5363,"endUtf16":5412,"startLine":155,"endLine":157}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5380:5390:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Base::value","startUtf16":5380,"endUtf16":5390,"startLine":156,"endLine":156}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5413:5462:class:Cell","kind":"class","file":"ScopeFixture.java","name":"Cell","qualified":"audit.Cell","startUtf16":5413,"endUtf16":5462,"startLine":158,"endLine":160}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5430:5440:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Cell::value","startUtf16":5430,"endUtf16":5440,"startLine":159,"endLine":159}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5463:5529:class:Shadow","kind":"class","file":"ScopeFixture.java","name":"Shadow","qualified":"audit.Shadow","startUtf16":5463,"endUtf16":5529,"startLine":161,"endLine":163}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5495:5505:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Shadow::value","startUtf16":5495,"endUtf16":5505,"startLine":162,"endLine":162}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5530:5633:class:Token","kind":"class","file":"ScopeFixture.java","name":"Token","qualified":"audit.Token","startUtf16":5530,"endUtf16":5633,"startLine":164,"endLine":167}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5573:5583:field:value","kind":"field","file":"ScopeFixture.java","name":"value","qualified":"audit.Token::value","startUtf16":5573,"endUtf16":5583,"startLine":165,"endLine":165}} +{"type":"declaration","declaration":{"id":"ScopeFixture.java:5609:5631:method:close","kind":"method","file":"ScopeFixture.java","name":"close","qualified":"audit.Token::close","startUtf16":5609,"endUtf16":5631,"startLine":166,"endLine":166}} +{"type":"complete","declarations":58,"fieldReferences":45,"unboundExpressions":0} diff --git a/benchmarks/agent_query/java_field_capture.py b/benchmarks/agent_query/java_field_capture.py new file mode 100644 index 000000000..0b84853f0 --- /dev/null +++ b/benchmarks/agent_query/java_field_capture.py @@ -0,0 +1,100 @@ +"""Capture the registered real-source field oracle with an installed JDK. + +Uses cached, digest-matched dependencies. No downloads, project code execution, +annotation processors or generated project classes. All outputs go to a new +explicit artifact directory; a failed process is retained and never scored. +""" +import argparse +import json +import os +from pathlib import Path +import subprocess + +from benchmarks.agent_query.java_source_fields import load_capture, sha, MAX_CAPTURE +from benchmarks.agent_query.state_access_audit import read, MAX_GRAPH_BYTES +from benchmarks.agent_query.runner import run_bounded + + +def capture(registration, root, java_home, classpath, artifacts): + reg = json.loads(read(registration, MAX_CAPTURE)) + if reg['schema'] != 'compass.java-real-field-registration/1': + raise ValueError('unknown compiler registration') + root, java_home = root.resolve(), java_home.resolve() + suffix = '.exe' if os.name == 'nt' else '' + java = java_home / 'bin' / ('java' + suffix) + javac = java_home / 'bin' / ('javac' + suffix) + def git(*args): + return subprocess.check_output(['git', '-C', str(root), *args], text=True, timeout=30).strip() + files = {row['file']: row['sha256'] for row in reg['files']} + if len(files) != len(reg['files']) or not 0 < len(files) <= 512: + raise ValueError('duplicate or excessive source files') + def check_sources(): + if git('rev-parse', 'HEAD') != reg['commit'] or git('status', '--porcelain'): + raise ValueError('source commit/status drift') + if sha(root / 'pom.xml') != reg['buildEvidence']['pomSha256']: + raise ValueError('build configuration drift') + total = 0 + for file, digest in files.items(): + path = (root / file).resolve() + path.relative_to(root) + data = read(path, 4 * 1024 * 1024) + total += len(data) + if sha(path) != digest or total > 64 * 1024 * 1024: + raise ValueError('source digest/size mismatch') + check_sources() + libraries = [p.resolve() for p in classpath] + expected = reg['buildEvidence']['classpath'] + if len(libraries) != len(expected) or any(sha(p) != row['sha256'] for p, row in zip(libraries, expected)): + raise ValueError('classpath does not match registration') + artifacts.mkdir(parents=True, exist_ok=False) + classes = artifacts / 'classes' + classes.mkdir() + tool = Path(__file__).parent / 'java_oracle/FieldBindings.java' + manifest = dict(schema='compass.java-field-capture/1', complete=False, + registrationSha256=sha(registration), toolSha256=sha(tool), + collectorSha256=sha(__file__), runnerSha256=sha(Path(__file__).with_name('runner.py')), + javaSha256=sha(java), javacSha256=sha(javac), + modulesSha256=sha(java_home / 'lib/modules', MAX_GRAPH_BYTES), + releaseSha256=sha(java_home / 'release'), files=files, + classpathSha256={row['coordinate']: sha(p) for p, row in zip(libraries, expected)}, commands=[]) + destination = artifacts / 'manifest.json' + def save(): + destination.write_text(json.dumps(manifest, indent=2) + '\n') + def call(name, argv): + result = run_bounded(tuple(map(str, argv)), cwd=artifacts, timeout_seconds=120, + stdout_path=artifacts / (name + '.stdout'), stderr_path=artifacts / (name + '.stderr')) + manifest['commands'].append(dict(name=name, argv=list(map(str, argv)), exitCode=result.exit_code, + timedOut=result.timed_out, outputLimited=result.output_limited, milliseconds=result.wall_ms, + stdoutSha256=sha(artifacts / (name + '.stdout')), stderrSha256=sha(artifacts / (name + '.stderr')))) + save() + if result.exit_code or result.timed_out or result.output_limited: + raise ValueError(f'{name} failed; retained logs are not a scored capture') + save() + call('compile', [javac, '--release', '17', '-proc:none', '-d', classes, tool.resolve()]) + manifest['classHashes'] = {str(p.relative_to(classes)): sha(p) for p in sorted(classes.rglob('*.class'))} + listing = artifacts / 'files.txt' + listing.write_text('\n'.join(sorted(files)) + '\n') + call('bindings', [java, '-Xmx1024m', '-cp', classes, 'FieldBindings', root, + reg['buildEvidence']['release'], os.pathsep.join(map(str, libraries)), listing]) + load_capture(artifacts / 'bindings.stdout', root, files) + check_sources() + manifest.update(complete=True, exitCode=0, timedOut=False, outputLimited=False, + stdoutSha256=sha(artifacts / 'bindings.stdout'), stderrSha256=sha(artifacts / 'bindings.stderr')) + save() + return manifest + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--registration', type=Path, required=True) + parser.add_argument('--root', type=Path, required=True) + parser.add_argument('--java-home', type=Path, required=True) + parser.add_argument('--classpath', type=Path, action='append', required=True) + parser.add_argument('--artifacts', type=Path, required=True) + args = parser.parse_args() + result = capture(args.registration, args.root, args.java_home, args.classpath, args.artifacts.resolve()) + print(json.dumps(dict(complete=result['complete'], files=len(result['files']), stdoutSha256=result['stdoutSha256']))) + + +if __name__ == '__main__': + main() diff --git a/benchmarks/agent_query/java_oracle/FieldBindings.java b/benchmarks/agent_query/java_oracle/FieldBindings.java new file mode 100644 index 000000000..712d95f3a --- /dev/null +++ b/benchmarks/agent_query/java_oracle/FieldBindings.java @@ -0,0 +1,221 @@ +/* Independent development oracle using public JDK compiler APIs. + * Parse and attribute source; never generate or execute project classes. + */ +import com.sun.source.tree.*; +import com.sun.source.util.*; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.util.*; +import javax.lang.model.element.*; +import javax.lang.model.type.TypeKind; +import javax.tools.*; + +public final class FieldBindings { + private static final int MAX_FILES = 512, MAX_RECORDS = 100000; + private static final long MAX_FILE = 4194304, MAX_TOTAL = 67108864; + private final Trees trees; + private final Path root; + private final Set inputs; + private final Map texts = new HashMap<>(); + private final Set declarations = new HashSet<>(); + private final Set occurrences = new HashSet<>(); + private int records; + private int unboundExpressions; + + private FieldBindings(Trees trees, Path root, Set inputs) { + this.trees = trees; + this.root = root; + this.inputs = inputs; + } + + private static Map object(Object... entries) { + Map map = new LinkedHashMap<>(); + for (int i = 0; i < entries.length; i += 2) + map.put((String) entries[i], entries[i + 1]); + return map; + } + + private static String json(Object value) { + if (value == null) return "null"; + if (value instanceof Number || value instanceof Boolean) return value.toString(); + if (value instanceof Map map) { + List parts = new ArrayList<>(); + for (var entry : map.entrySet()) parts.add(json(entry.getKey()) + ":" + json(entry.getValue())); + return "{" + String.join(",", parts) + "}"; + } + String text = value.toString(); + StringBuilder out = new StringBuilder("\""); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '\\' || c == '"') out.append('\\').append(c); + else if (c < 32) out.append(String.format(Locale.ROOT, "\\u%04x", (int) c)); + else out.append(c); + } + return out.append('"').toString(); + } + + private void emit(Map record) { + if (++records > MAX_RECORDS) throw new IllegalStateException("record limit"); + System.out.println(json(record)); + } + + private String file(CompilationUnitTree unit) { + try { + Path path = Path.of(unit.getSourceFile().toUri()).toRealPath(); + if (!inputs.contains(path)) throw new IllegalStateException("unregistered source: " + path); + return root.relativize(path).toString().replace(File.separatorChar, '/'); + } catch (IOException ex) { throw new UncheckedIOException(ex); } + } + + private String text(CompilationUnitTree unit) { + return texts.computeIfAbsent(file(unit), key -> { + try { return unit.getSourceFile().getCharContent(false).toString(); } + catch (IOException ex) { throw new UncheckedIOException(ex); } + }); + } + + private static boolean field(Element element) { + return element != null && (element.getKind() == ElementKind.FIELD + || element.getKind() == ElementKind.ENUM_CONSTANT); + } + + private static String qualified(Element element) { + if (element instanceof TypeElement type) return type.getQualifiedName().toString(); + if (element == null) return ""; + return qualified(element.getEnclosingElement()) + "::" + element.getSimpleName(); + } + + private Map declaration(TreePath path) { + if (path == null) return null; + CompilationUnitTree unit = path.getCompilationUnit(); + long start = trees.getSourcePositions().getStartPosition(unit, path.getLeaf()); + long end = trees.getSourcePositions().getEndPosition(unit, path.getLeaf()); + Element element = trees.getElement(path); + if (element == null || start < 0 || end <= start || end > text(unit).length()) return null; + String name = element.getSimpleName().toString(); + String kind = element.getKind().toString().toLowerCase(Locale.ROOT); + String file = file(unit); + String id = file + ":" + start + ":" + end + ":" + kind + ":" + name; + return object("id", id, "kind", kind, "file", file, "name", name, + "qualified", qualified(element), "startUtf16", start, "endUtf16", end, + "startLine", unit.getLineMap().getLineNumber(start), + "endLine", unit.getLineMap().getLineNumber(end - 1)); + } + + private Map owner(TreePath path) { + for (TreePath parent = path.getParentPath(); parent != null; parent = parent.getParentPath()) { + Tree leaf = parent.getLeaf(); + if (leaf instanceof MethodTree || leaf instanceof ClassTree + || (leaf instanceof VariableTree && field(trees.getElement(parent)))) + return declaration(parent); + } + return null; + } + + private void declare(TreePath path) { + Map value = declaration(path); + if (value != null && declarations.add((String) value.get("id"))) + emit(object("type", "declaration", "declaration", value)); + } + + private void access(TreePath path, String spelling) { + if (spelling.equals("this") || spelling.equals("super")) return; + Element element = trees.getElement(path); + if (element == null) { unboundExpressions++; return; } + if (!field(element)) return; + CompilationUnitTree unit = path.getCompilationUnit(); + long start = trees.getSourcePositions().getStartPosition(unit, path.getLeaf()); + long end = trees.getSourcePositions().getEndPosition(unit, path.getLeaf()); + // Attribute-generated trees are not source occurrences. + if (start < 0 || end <= start || end > text(unit).length()) return; + long tokenStart = path.getLeaf() instanceof IdentifierTree ? start : end - spelling.length(); + boolean anchored = tokenStart >= start && text(unit).substring((int) tokenStart, (int) end).equals(spelling); + String key = file(unit) + ":" + start + ":" + end; + if (!occurrences.add(key)) throw new IllegalStateException("duplicate occurrence: " + key); + Map target = declaration(trees.getPath(element)); + String origin = target != null ? "source" : "external"; + if (target == null && path.getLeaf() instanceof MemberSelectTree select) { + if (spelling.equals("class")) origin = "class-literal"; + else if (spelling.equals("length") && trees.getTypeMirror( + new TreePath(path, select.getExpression())).getKind() == TypeKind.ARRAY) + origin = "array-length"; + } + emit(object("type", "fieldReference", "file", file(unit), "name", spelling, + "expressionStartUtf16", start, "expressionEndUtf16", end, + "tokenStartUtf16", anchored ? tokenStart : null, "tokenEndUtf16", anchored ? end : null, + "anchorStatus", anchored ? "exact" : "unsupported-raw-spelling", + "line", unit.getLineMap().getLineNumber(end - 1), + "targetKind", element.getKind().toString().toLowerCase(Locale.ROOT), + "targetQualified", qualified(element), "targetOrigin", origin, "target", target, "owner", owner(path))); + } + + private final class Scanner extends TreePathScanner { + private int depth; + @Override public Void scan(Tree tree, Void unused) { + if (++depth > 512) throw new IllegalStateException("AST depth limit"); + try { return super.scan(tree, unused); } + finally { depth--; } + } + @Override public Void visitImport(ImportTree tree, Void unused) { return null; } + @Override public Void visitClass(ClassTree tree, Void unused) { + declare(getCurrentPath()); return super.visitClass(tree, unused); + } + @Override public Void visitMethod(MethodTree tree, Void unused) { + declare(getCurrentPath()); return super.visitMethod(tree, unused); + } + @Override public Void visitVariable(VariableTree tree, Void unused) { + if (field(trees.getElement(getCurrentPath()))) declare(getCurrentPath()); + return super.visitVariable(tree, unused); + } + @Override public Void visitIdentifier(IdentifierTree tree, Void unused) { + access(getCurrentPath(), tree.getName().toString()); return super.visitIdentifier(tree, unused); + } + @Override public Void visitMemberSelect(MemberSelectTree tree, Void unused) { + access(getCurrentPath(), tree.getIdentifier().toString()); return super.visitMemberSelect(tree, unused); + } + } + + public static void main(String[] args) throws Exception { + if (args.length != 4) throw new IllegalArgumentException("root release classpath file-list"); + System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8)); + Path root = Path.of(args[0]).toRealPath(); + List names = Files.readAllLines(Path.of(args[3]), StandardCharsets.UTF_8); + if (names.isEmpty() || names.size() > MAX_FILES) throw new IllegalArgumentException("file count"); + SortedSet paths = new TreeSet<>(); + long bytes = 0; + for (String name : names) { + Path path = root.resolve(name).toRealPath(); + long size = Files.size(path); + if (!path.startsWith(root) || !name.endsWith(".java") || size > MAX_FILE || !paths.add(path)) + throw new IllegalArgumentException("invalid source " + name); + bytes += size; + } + if (bytes > MAX_TOTAL) throw new IllegalArgumentException("source byte limit"); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) throw new IllegalStateException("JDK compiler required"); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + try (StandardJavaFileManager manager = compiler.getStandardFileManager(diagnostics, Locale.ROOT, StandardCharsets.UTF_8)) { + JavacTask task = (JavacTask) compiler.getTask(new PrintWriter(System.err), manager, diagnostics, + List.of("--release", args[1], "-encoding", "UTF-8", "-proc:none", "-implicit:none", + "-sourcepath", "", "-classpath", args[2], "-Xmaxerrs", "100", "-Xmaxwarns", "100"), + null, manager.getJavaFileObjectsFromPaths(paths)); + List units = new ArrayList<>(); + task.parse().forEach(units::add); + task.analyze(); + boolean errors = false; + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + errors |= diagnostic.getKind() == Diagnostic.Kind.ERROR; + System.err.println(diagnostic.toString()); + } + if (errors) throw new IllegalStateException("compiler errors; no scored oracle"); + FieldBindings oracle = new FieldBindings(Trees.instance(task), root, paths); + oracle.emit(object("type", "header", "schema", "compass.javac-field-bindings/1", + "files", paths.size(), "sourceBytes", bytes, "release", args[1], + "positionEncoding", "UTF-16 code units", "compilerRuntime", Runtime.version().toString())); + for (CompilationUnitTree unit : units) oracle.new Scanner().scan(unit, null); + oracle.emit(object("type", "complete", "declarations", oracle.declarations.size(), + "fieldReferences", oracle.occurrences.size(), "unboundExpressions", oracle.unboundExpressions)); + } + } +} diff --git a/benchmarks/agent_query/java_real_field_review.json b/benchmarks/agent_query/java_real_field_review.json new file mode 100644 index 000000000..a1972f66f --- /dev/null +++ b/benchmarks/agent_query/java_real_field_review.json @@ -0,0 +1,209 @@ +{ + "schema": "compass.java-real-field-review/1", + "scope": "Complete Java 8 base-source field-reference audit on a previously observed jsoup development repository and frozen graphs. Registered before compiler source binding capture; not held-out or an independent human review. Do not combine with five-language site recall or treat as god-object classification.", + "registrationCommit": "2499e927", + "registrationSha256": "6088df9f4cf420437a71ca06324bbe80817ed73142eb61c5686d8d3da36180af", + "productCommit": "f2cacfebba15d69a738e7c14694d51320e4e07ba", + "baselineBranchCommit": "d278c1e9f1e9c99e8a3ca07a7ebd4ceda40f5b63", + "sourceCommit": "37aea49902972cec9a53dc2c65023729f1c3715b", + "artifactDirectory": "jsoup-java-field-oracle-02", + "sourceFiles": 88, + "sourceBytes": 1150637, + "compilerRuntime": "17.0.8.1+8-LTS", + "compilerRelease": "8", + "compilerRecords": { + "type": "complete", + "declarations": 3356, + "fieldReferences": 4444, + "unboundExpressions": 0 + }, + "origins": { + "source": 4314, + "array-length": 55, + "external": 44, + "class-literal": 31 + }, + "results": { + "compass": { + "summary": { + "enum_constant": { + "occurrences": 529, + "exactTargetAndOwner": 0, + "declarations": 131, + "uniqueGraphDeclarations": 131, + "unsupportedAnchors": 0 + }, + "field": { + "occurrences": 3785, + "exactTargetAndOwner": 3047, + "declarations": 616, + "uniqueGraphDeclarations": 614, + "unsupportedAnchors": 0 + } + }, + "contactStatus": { + "verified_target_and_owner": 3047 + }, + "graphSha256": "2a9216926b1c5dfc82e4f88df0cce24a7a14ac67aa2a350158eff39018145314", + "graphDirected": true + }, + "graphify": { + "summary": { + "enum_constant": { + "occurrences": 529, + "exactTargetAndOwner": 0, + "declarations": 131, + "uniqueGraphDeclarations": 131, + "unsupportedAnchors": 0 + }, + "field": { + "occurrences": 3785, + "exactTargetAndOwner": 0, + "declarations": 616, + "uniqueGraphDeclarations": 0, + "unsupportedAnchors": 0 + } + }, + "contactStatus": {}, + "graphSha256": "2a06daf207c92172900179a308c0d4816366b045aa94c9e08c610e3f6185cf69", + "graphDirected": false + } + }, + "missingOrdinaryFields": [ + { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "line": 473, + "name": "expectedSize", + "qualified": "::expectedSize" + }, + { + "file": "src/main/java/org/jsoup/nodes/Attributes.java", + "line": 474, + "name": "i", + "qualified": "::i" + } + ], + "mostMissedOrdinaryTargets": [ + { + "qualified": "org.jsoup.parser.TreeBuilder::stack", + "occurrences": 51 + }, + { + "qualified": "org.jsoup.parser.TokeniserState::nullChar", + "occurrences": 47 + }, + { + "qualified": "org.jsoup.parser.TokeniserState::eof", + "occurrences": 41 + }, + { + "qualified": "org.jsoup.parser.Parser::NamespaceHtml", + "occurrences": 32 + }, + { + "qualified": "org.jsoup.parser.Token.Doctype::forceQuirks", + "occurrences": 31 + }, + { + "qualified": "org.jsoup.parser.TokeniserState::replacementChar", + "occurrences": 26 + }, + { + "qualified": "org.jsoup.parser.Token.Tag::normalName", + "occurrences": 21 + }, + { + "qualified": "org.jsoup.parser.TreeBuilder::settings", + "occurrences": 16 + }, + { + "qualified": "org.jsoup.select.Evaluator.AttributeKeyPair::key", + "occurrences": 14 + }, + { + "qualified": "org.jsoup.nodes.Node::parentNode", + "occurrences": 13 + }, + { + "qualified": "org.jsoup.parser.TreeBuilder::tokeniser", + "occurrences": 13 + }, + { + "qualified": "org.jsoup.parser.Token.Tag::attributes", + "occurrences": 13 + }, + { + "qualified": "org.jsoup.select.StructuralEvaluator::evaluator", + "occurrences": 11 + }, + { + "qualified": "org.jsoup.parser.Parser::NamespaceSvg", + "occurrences": 10 + }, + { + "qualified": "org.jsoup.parser.TreeBuilder::doc", + "occurrences": 10 + }, + { + "qualified": "org.jsoup.select.Evaluator.AttributeKeyPair::value", + "occurrences": 10 + }, + { + "qualified": "::i", + "occurrences": 9 + }, + { + "qualified": "org.jsoup.internal.SharedConstants::DefaultBufferSize", + "occurrences": 8 + }, + { + "qualified": "org.jsoup.parser.Parser::NamespaceMathml", + "occurrences": 8 + }, + { + "qualified": "org.jsoup.helper.DataUtil::UTF_8", + "occurrences": 7 + } + ], + "qualification": { + "sourceBytecodeCases": 44, + "sourceBytecodeOccurrences": 45, + "adversarialRecords": 20, + "unitTests": 176, + "productBoundaryPassed": true, + "repeatedCaptureByteIdentical": true, + "offlineReviewsReplayed": true + }, + "limitations": [ + "Previously observed development repository; this is a compiler-backed source census for one build configuration, not held-out or five-language generalization.", + "All native graph references/reads/writes to field or enum-member endpoints in the registered files are inventoried. Unmapped, ambiguous, wrong and unanchored records must be retained. No such failure occurred among the 3047 Compass contacts here; Graphify has no such contacts and no positive-contact precision denominator.", + "Declarations use source regions and names; exact occurrence scoring requires target and owner identities plus UTF-8 spans. Line-only and unordered records are not upgraded to exact/directed proof.", + "JDK/base-source version and cached dependencies are pinned. Java 11 overlay, metadata, test sources, external fields and compiler intrinsics are separate from the 4314 internal-reference denominator.", + "Read/write effects, runtime aliasing, explanations, graph traversal, functional community quality and god-object judgments remain unscored. Missing field contacts cannot prove independent responsibilities.", + "Round 01 mixed array-length and class-literal pseudo-fields into the non-source group; retained as superseded captures. Round 02 separates them. The internal denominator and comparison results remain unchanged.", + "This checkpoint changes only the evaluation harness, fixtures and documentation. Native Rust/JS/platform/packaging gates were not rerun." + ], + "supportSha256": { + "java_field_capture.py": "535e43b75cc4aa09349f2086025781bd2301cca923571d9565659849a35c1c06", + "java_source_fields.py": "2ced23208ab1171d33ec00a6385fce34367a87c67d2cadef249650a1b8264e9b", + "java_oracle/FieldBindings.java": "0616d29730f77fc33a313e5c3280d6905c5435433f8e52ac4250ce2a8124e9d6", + "tests/test_java_source_fields.py": "b276d9e2be309c41a7102201deba709923db8e8cce74afa6ae3b635ecf130bd4", + "runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "state_access_audit.py": "2bf95b707f18f34c9df4461a26f011b83d86f166efacfaab652f8e6231161058" + }, + "artifactSha256": { + "capture-final/manifest.json": "6b9532e36846aa7c2df0ac799c1c9f01dbf8c5061dd3adfa5a0fbc8e8bb64d3a", + "capture-final/bindings.stdout": "4596b24d67fab7f728bc5dfe54e9b381fc42d411b9cb9042646cffbed420e95c", + "capture-final/bindings.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "capture-repeat/manifest.json": "9790bfc817fabd951e733f4a2b2381dfb03d8fba8c88c3f03a8ee7cf35e037ec", + "scope.stdout": "f9add7ff7e6b7158f6a0afdc8be51c439417952b7f4a26dad8eedd047484b1c6", + "scope-manifest.json": "35346f31a8bf1d6285dff4fe948445c08046f824b1e8671d486328429390f864", + "adversarial.stdout": "be9b847db2aa9a8a298c9fe2c91453250e3c8b2d8ba89dbca87c787e6f5f7967", + "adversarial-manifest.json": "258b27cf9637ac8e5f5f0af7705372bd1a459730f987fbbf652f8436bb8fe3b0", + "compass-review.json": "a08b07df8b4e22eebbd7934867fc188dd321a9fd97f93ccfcc6d49025f0be7a0", + "graphify-review.json": "7c7aa583a7ce0394f3bd2c1830a34e0d02cb618602d33feae035b9699e1137e1", + "final-tests.log": "df327030d1d1fc29759f586cf5a94946d0d121b4cec3784b2d51a06566d4159c", + "product-boundary.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "verify.py": "a43d8bb199b3625875c7470e34ef1bb0414ed135ce802e6e1f53c58ddbd695d6" + } +} diff --git a/benchmarks/agent_query/java_source_fields.py b/benchmarks/agent_query/java_source_fields.py new file mode 100644 index 000000000..01ce838d9 --- /dev/null +++ b/benchmarks/agent_query/java_source_fields.py @@ -0,0 +1,290 @@ +"""Replay public-JDK source field bindings and compare frozen native graphs. + +Development evidence only. Exact occurrences, targets and owners are distinct +from graph consistency, line-only support, read/write effects and design quality. +The compiler is needed for capture, never for offline replay or product runtime. +""" +import argparse +from array import array +from bisect import bisect_right +from collections import Counter, defaultdict +import hashlib +import json +from pathlib import Path + +from benchmarks.agent_query.state_access_audit import read, name, anchor, MAX_GRAPH_BYTES + +MAX_SOURCE = 4 * 1024 * 1024 +MAX_CAPTURE = 16 * 1024 * 1024 +MAX_RECORDS = 100000 + + +def sha(path, limit=MAX_CAPTURE): + return hashlib.sha256(read(Path(path), limit)).hexdigest() + + +def utf16_to_bytes(text): + """Map Java character positions to UTF-8 bytes; reject split surrogates.""" + result = array('q', [0]) + total = 0 + for char in text: + if ord(char) > 0xffff: + result.append(-1) + total += len(char.encode('utf-8')) + result.append(total) + return result + + +def position(mapping, value): + if type(value) is not int or not 0 <= value < len(mapping) or mapping[value] < 0: + raise ValueError('invalid UTF-16 source boundary') + return mapping[value] + + +def load_capture(path, root, source_hashes): + if not 0 < len(source_hashes) <= 512: + raise ValueError('source count limit') + sources, mappings, lines = {}, {}, {} + root = root.resolve() + total = 0 + for file, digest in source_hashes.items(): + p = (root / file).resolve() + p.relative_to(root) + raw = read(p, MAX_SOURCE) + total += len(raw) + if total > 64 * 1024 * 1024 or hashlib.sha256(raw).hexdigest() != digest: + raise ValueError('source drift or byte limit') + sources[file] = raw + mappings[file] = utf16_to_bytes(raw.decode('utf-8')) + lines[file] = [0] + [i + 1 for i, byte in enumerate(raw) if byte == 10] + rows = [json.loads(line) for line in read(path, MAX_CAPTURE).splitlines()] + if not 2 <= len(rows) <= MAX_RECORDS: + raise ValueError('capture record count') + header, complete = rows[0], rows[-1] + if (header.get('type') != 'header' or header.get('schema') != 'compass.javac-field-bindings/1' + or header.get('positionEncoding') != 'UTF-16 code units' + or header.get('files') != len(sources) or header.get('sourceBytes') != total + or complete.get('type') != 'complete' or complete.get('unboundExpressions') != 0): + raise ValueError('incomplete or incompatible compiler capture') + + def declaration(raw): + if raw is None: + return None + item = dict(raw) + file = item['file'] + mapping = mappings[file] + start, end = position(mapping, item['startUtf16']), position(mapping, item['endUtf16']) + if start >= end: + raise ValueError('empty declaration') + expected_id = f"{file}:{item['startUtf16']}:{item['endUtf16']}:{item['kind']}:{item['name']}" + if (item['id'] != expected_id or item['startLine'] != bisect_right(lines[file], start) + or item['endLine'] != bisect_right(lines[file], end - 1)): + raise ValueError('declaration identity/line mismatch') + item.update(startByte=start, endByte=end) + return item + + declarations, references, seen = {}, [], set() + for raw in rows[1:-1]: + if raw.get('type') == 'declaration': + item = declaration(raw['declaration']) + if item['id'] in declarations: + raise ValueError('duplicate declaration') + declarations[item['id']] = item + elif raw.get('type') == 'fieldReference': + item = dict(raw) + file = item['file'] + mapping = mappings[file] + a, b = position(mapping, item['expressionStartUtf16']), position(mapping, item['expressionEndUtf16']) + key = (file, a, b) + if a >= b or key in seen or item['line'] != bisect_right(lines[file], b - 1): + raise ValueError('duplicate or invalid field occurrence') + seen.add(key) + if item['anchorStatus'] == 'exact': + start, end = position(mapping, item['tokenStartUtf16']), position(mapping, item['tokenEndUtf16']) + if not a <= start < end <= b or sources[file][start:end].decode() != item['name']: + raise ValueError('field token mismatch') + item.update(startByte=start, endByte=end) + elif item['anchorStatus'] == 'unsupported-raw-spelling': + if item['tokenStartUtf16'] is not None or item['tokenEndUtf16'] is not None: + raise ValueError('unsupported anchor supplied coordinates') + item.update(startByte=None, endByte=None) + else: + raise ValueError('unknown anchor status') + item['target'] = declaration(item['target']) + item['owner'] = declaration(item['owner']) + if item.get('targetOrigin') not in {'source', 'external', 'array-length', 'class-literal'} or (item['targetOrigin'] == 'source') != bool(item['target']): + raise ValueError('invalid target origin') + item.update(expressionStartByte=a, expressionEndByte=b) + if item['target'] and item['target']['kind'] != item['targetKind']: + raise ValueError('target kind mismatch') + if item['owner'] and not (item['owner']['file'] == file + and item['owner']['startByte'] <= a < b <= item['owner']['endByte']): + raise ValueError('source owner does not contain occurrence') + references.append(item) + else: + raise ValueError('unknown capture record') + if complete.get('declarations') != len(declarations) or complete.get('fieldReferences') != len(references): + raise ValueError('compiler completion count mismatch') + for row in references: + for key in ('target', 'owner'): + if row[key] and declarations.get(row[key]['id']) != row[key]: + raise ValueError('unregistered compiler declaration') + return dict(header=header, complete=complete, declarations=declarations, references=references) + + +def compatible_kind(node, tool, declaration): + kind = declaration['kind'] + if tool == 'compass': + return node.get('kind') == { + 'enum_constant': 'enum_member', 'annotation_type': 'annotation', + }.get(kind, kind) + # Only use explicit native flags; absent type metadata is not invented. + if node.get('_callable_class'): + return kind in {'class', 'interface', 'enum', 'annotation_type', 'record'} + if node.get('_callable'): + return kind in {'method', 'constructor'} + return True + + +def join_declarations(graph, tool, declarations): + by_file_name = defaultdict(list) + for declaration in declarations.values(): + aliases = {declaration['name']} + if declaration['kind'] == 'constructor': + aliases.add(declaration['qualified'].rsplit('::', 1)[0].rsplit('.', 1)[-1]) + for alias in aliases: + by_file_name[declaration['file'], alias].append(declaration) + node_to_decls, decl_to_nodes = {}, defaultdict(list) + for node in graph['nodes']: + file, line = anchor(node, tool) + matches = [] + for declaration in by_file_name.get((file, name(node, tool)), []): + if not compatible_kind(node, tool, declaration): + continue + if tool == 'compass': + point = node.get('source', {}).get('startByte') + inside = type(point) is int and declaration['startByte'] <= point < declaration['endByte'] + else: + inside = type(line) is int and declaration['startLine'] <= line <= declaration['endLine'] + if inside: + matches.append(declaration['id']) + decl_to_nodes[declaration['id']].append(node['id']) + node_to_decls[node['id']] = sorted(matches) + return node_to_decls, {key: sorted(value) for key, value in decl_to_nodes.items()} + + +def compare(graph, tool, capture, files): + nodes = {node['id']: node for node in graph['nodes']} + if len(nodes) != len(graph['nodes']) or len(graph['nodes']) > 1000000 or len(graph['links']) > 5000000: + raise ValueError('duplicate node or graph size limit') + if any(e['source'] not in nodes or e['target'] not in nodes for e in graph['links']): + raise ValueError('missing graph endpoint') + declarations, references = capture['declarations'], capture['references'] + node_join, decl_join = join_declarations(graph, tool, declarations) + field_kinds = {'field', 'enum_constant'} + site_index = defaultdict(list) + for i, row in enumerate(references): + if row['startByte'] is not None: + site_index[row['file'], row['startByte'], row['endByte']].append(i) + contacts, matched = [], set() + for i, edge in enumerate(graph['links']): + if edge.get('kind' if tool == 'compass' else 'relation') not in {'references', 'reads', 'writes'}: + continue + target, source = nodes[edge['target']], nodes[edge['source']] + def is_field(node): + return (node.get('kind') in {'field', 'enum_member'} or node.get('symbol_kind') == 'field' + or any(declarations[d]['kind'] in field_kinds for d in node_join[node['id']])) + if not is_field(target): + if tool == 'graphify' and graph.get('directed') is False and is_field(source): + # Inventory an unordered contact in either stored orientation. + # It still has no exact/directed occurrence proof. + source, target = target, source + else: + continue + targets, owners = node_join[target['id']], node_join[source['id']] + site = edge.get('relationshipSite', {}) if tool == 'compass' else {} + file, line = ((site.get('file'), site.get('startLine')) if tool == 'compass' else anchor(edge, tool)) + if file not in files: + if file is not None or anchor(source, tool)[0] not in files: + continue + file = anchor(source, tool)[0] + expected = site_index.get((file, site.get('startByte'), site.get('endByte')), []) + status = 'unanchored' if not site else 'unmatched_occurrence' + if expected: + if len(expected) != 1: + status = 'ambiguous_occurrence' + elif len(targets) != 1 or len(decl_join.get(targets[0], [])) != 1: + status = 'unmapped_target' if not targets else 'ambiguous_target' + else: + row = references[expected[0]] + if row['target'] is None or row['target']['id'] != targets[0]: + status = 'wrong_target' + elif len(owners) != 1 or row['owner'] is None or len(decl_join.get(owners[0], [])) != 1: + status = 'unmapped_owner' if not owners else 'ambiguous_owner' + elif row['owner']['id'] != owners[0]: + status = 'wrong_owner' + else: + status = 'verified_target_and_owner' + matched.add(expected[0]) + contacts.append(dict(index=i, id=edge.get('id'), source=edge['source'], target=edge['target'], + file=file, line=line, site=site, graphDirected=graph.get('directed'), targetDeclarations=targets, + ownerDeclarations=owners, oracleReferences=expected, status=status)) + internal = [(i, row) for i, row in enumerate(references) if row['target']] + summary = {} + for kind in sorted(field_kinds): + rows = [(i, r) for i, r in internal if r['targetKind'] == kind] + fields = [d for d in declarations.values() if d['kind'] == kind] + summary[kind] = dict(occurrences=len(rows), exactTargetAndOwner=sum(i in matched for i, _ in rows), + declarations=len(fields), uniqueGraphDeclarations=sum(len(decl_join.get(d['id'], [])) == 1 for d in fields), + unsupportedAnchors=sum(r['startByte'] is None for _, r in rows)) + misses = [] + for i, row in internal: + if i in matched: + continue + target_candidates = decl_join.get(row['target']['id'], []) + owner_candidates = decl_join.get(row['owner']['id'], []) if row['owner'] else [] + misses.append(dict(oracleReference=i, file=row['file'], line=row['line'], name=row['name'], + target=row['target']['id'], owner=row['owner']['id'] if row['owner'] else None, + targetCandidates=target_candidates, ownerCandidates=owner_candidates)) + return dict(schema='compass.java-source-field-review/1', tool=tool, graphDirected=graph.get('directed'), identityPolicy='name/kind and byte region for Compass; explicit native flags and name/line region for Graphify; ambiguous joins never select a winner', + summary=summary, contactStatus=dict(sorted(Counter(c['status'] for c in contacts).items())), + contacts=contacts, misses=misses, declarationJoins=decl_join, + limits='Exact-span semantic evidence only. Line-only contacts remain unanchored and are not silently expanded into repeated occurrences. No inference about read/write effects, paths, cohesion, god-object defects or overall superiority.') + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--capture', type=Path, required=True) + parser.add_argument('--root', type=Path, required=True) + parser.add_argument('--manifest', type=Path, required=True) + parser.add_argument('--graph', type=Path) + parser.add_argument('--tool', choices=['compass', 'graphify']) + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--verify', action='store_true', help='recompute and compare an existing output') + args = parser.parse_args() + manifest = json.loads(read(args.manifest, MAX_CAPTURE)) + expected_hash = manifest.get('stdoutSha256', manifest.get('captureSha256')) + if not expected_hash or sha(args.capture) != expected_hash: + raise ValueError('capture drift or missing digest') + if manifest.get('exitCode', 0) != 0 or manifest.get('timedOut') or manifest.get('outputLimited'): + raise ValueError('failed or incomplete capture process') + capture = load_capture(args.capture, args.root, manifest['files']) + if args.graph: + if not args.tool: + parser.error('--graph requires --tool') + report = compare(json.loads(read(args.graph, MAX_GRAPH_BYTES)), args.tool, capture, manifest['files']) + report.update(graphSha256=sha(args.graph, MAX_GRAPH_BYTES)) + else: + report = capture + report.update(captureSha256=sha(args.capture), manifestSha256=sha(args.manifest), auditorSha256=sha(__file__)) + payload = (json.dumps(report, indent=2) + '\n').encode() + if args.verify: + if read(args.output, MAX_GRAPH_BYTES) != payload: + raise ValueError('replay differs from saved result') + else: + with args.output.open('xb') as output: + output.write(payload) + + +if __name__ == '__main__': + main() diff --git a/benchmarks/agent_query/tests/test_java_source_fields.py b/benchmarks/agent_query/tests/test_java_source_fields.py new file mode 100644 index 000000000..66e9228b3 --- /dev/null +++ b/benchmarks/agent_query/tests/test_java_source_fields.py @@ -0,0 +1,169 @@ +import copy +from collections import Counter +import json +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.java_source_fields import ( + compare, load_capture, position, sha, utf16_to_bytes, +) + +FIXTURES = Path('benchmarks/agent_query/fixtures/java_field_bindings') + + +class JavaSourceFieldsTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.manifest = json.loads((FIXTURES / 'adversarial-manifest.json').read_text()) + cls.capture = load_capture(FIXTURES / 'adversarial.jsonl', FIXTURES, cls.manifest['files']) + cls.raw = [json.loads(s) for s in (FIXTURES / 'adversarial.jsonl').read_text().splitlines()] + + def test_utf16_conversion_rejects_split_surrogates_and_non_integer_positions(self): + offsets = utf16_to_bytes('a🧭λ') + self.assertEqual(list(offsets), [0, 1, -1, 5, 7]) + for bad in [2, -1, 5, True, None]: + with self.subTest(bad=bad), self.assertRaises(ValueError): + position(offsets, bad) + self.assertEqual(position(offsets, 4), 7) + + def test_fixture_capture_hash_and_all_44_registered_compiler_cases(self): + manifest = json.loads((FIXTURES / 'scope-manifest.json').read_text()) + self.assertEqual(sha(FIXTURES / 'scope.jsonl'), manifest['captureSha256']) + capture = load_capture(FIXTURES / 'scope.jsonl', + Path('benchmarks/agent_query/fixtures/java_state_access'), manifest['files']) + reg = json.loads(Path('benchmarks/agent_query/java_state_scope_registration.json').read_text()) + fields = {(f['line'], f['name']): key for key, f in reg['fields'].items()} + for case in reg['cases']: + actual = [fields[r['target']['startLine'], r['target']['name']] + for r in capture['references'] if r['line'] == case['line'] and r['target']] + self.assertEqual(Counter(actual), Counter(case['expectedFields']), case['id']) + self.assertEqual(len(capture['references']), 45) + self.assertEqual(sum(not c['expectedFields'] for c in reg['cases']), 8) + + def test_adversarial_source_owners_and_overloads(self): + refs = self.capture['references'] + rows = {line: [r for r in refs if r['line'] == line] for line in [9, 10, 11, 12, 19, 24, 30]} + self.assertEqual(rows[9][0]['owner']['kind'], 'field') + self.assertEqual(rows[10][0]['owner']['kind'], 'class') + self.assertNotEqual(rows[11][0]['owner']['id'], rows[12][0]['owner']['id']) + self.assertEqual(rows[19][0]['owner']['name'], 'lambda') + for line in [24, 30]: + self.assertEqual(rows[line][0]['owner']['name'], 'get') + self.assertEqual(rows[line][0]['target']['startLine'], line - 1) + + def test_compound_multiplicity_constant_folding_and_enum_constants(self): + refs = self.capture['references'] + self.assertEqual([r['name'] for r in refs if r['line'] == 13], ['left', 'right', 'left']) + self.assertEqual([r['name'] for r in refs if r['line'] == 14], ['LIMIT']) + self.assertEqual([r['targetKind'] for r in refs if r['line'] == 35], ['enum_constant']) + self.assertEqual([r['target']['qualified'] for r in refs if r['line'] == 20], + ['bindings.Base::left', 'bindings.BindingFixture::left']) + + def test_external_and_unsupported_raw_spelling_are_preserved(self): + external = [r for r in self.capture['references'] if r['line'] == 15] + self.assertEqual(len(external), 1) + self.assertIsNone(external[0]['target']) + escaped = [r for r in self.capture['references'] if r['line'] == 18][0] + self.assertEqual(escaped['target']['name'], 'x') + self.assertIsNone(escaped['startByte']) + self.assertEqual(len(self.capture['references']), 20) + self.assertEqual(Counter(r['targetOrigin'] for r in self.capture['references']), + {'source': 17, 'external': 1, 'array-length': 1, 'class-literal': 1}) + self.assertEqual(sha(FIXTURES / 'adversarial.jsonl'), self.manifest['captureSha256']) + self.assertEqual(sha(Path('benchmarks/agent_query/java_oracle/FieldBindings.java')), self.manifest['toolSha256']) + + def mutated(self, mutate): + rows = copy.deepcopy(self.raw) + mutate(rows) + with tempfile.TemporaryDirectory() as directory: + p = Path(directory) / 'capture.jsonl' + p.write_text('\n'.join(json.dumps(r) for r in rows) + '\n') + return load_capture(p, FIXTURES, self.manifest['files']) + + def test_incomplete_unknown_schema_and_count_drift_fail(self): + for mutate in [lambda r: r.pop(), lambda r: r[0].update(schema='unknown/2'), + lambda r: r[-1].update(fieldReferences=0), + lambda r: r[-1].update(unboundExpressions=1)]: + with self.subTest(mutate=mutate), self.assertRaises(ValueError): + self.mutated(mutate) + + def test_duplicate_occurrence_and_corrupt_anchor_fail(self): + def duplicate(rows): + rows.insert(-1, next(r for r in rows if r['type'] == 'fieldReference')) + def corrupt(rows): + next(r for r in rows if r['type'] == 'fieldReference')['tokenStartUtf16'] += 1 + for mutate in [duplicate, corrupt]: + with self.subTest(mutate=mutate), self.assertRaises(ValueError): + self.mutated(mutate) + + def test_source_hash_drift_is_not_an_empty_oracle(self): + with self.assertRaises(ValueError): + load_capture(FIXTURES / 'adversarial.jsonl', FIXTURES, {'BindingFixture.java': '0' * 64}) + + def graph(self): + row = next(r for r in self.capture['references'] if r['line'] == 11) + def node(id, decl): + return dict(id=id, kind=decl['kind'], name=decl['name'], source=dict( + file=decl['file'], startByte=decl['startByte'], endByte=decl['endByte'], + startLine=decl['startLine'], endLine=decl['endLine'])) + graph = dict(directed=True, nodes=[node('method', row['owner']), node('field', row['target'])], + links=[dict(id='edge', source='method', target='field', kind='references', + relationshipSite=dict(file=row['file'], startByte=row['startByte'], + endByte=row['endByte'], startLine=row['line']))]) + return graph + + def assess(self, graph, tool='compass'): + return compare(graph, tool, self.capture, self.manifest['files']) + + def test_exact_target_owner_and_occurrence_are_jointly_required(self): + result = self.assess(self.graph()) + self.assertEqual(result['contactStatus'], {'verified_target_and_owner': 1}) + self.assertEqual(result['summary']['field']['exactTargetAndOwner'], 1) + self.assertEqual(result['summary']['field']['unsupportedAnchors'], 1) + + def test_wrong_target_and_wrong_owner_are_distinct(self): + for which, status in [('field', 'wrong_target'), ('method', 'wrong_owner')]: + graph = self.graph() + row = next(r for r in self.capture['references'] if r['line'] == 12) + decl = row['target'] if which == 'field' else row['owner'] + node = next(n for n in graph['nodes'] if n['id'] == which) + node.update(name=decl['name'], source=dict(file=decl['file'], startByte=decl['startByte'], + endByte=decl['endByte'], startLine=decl['startLine'], endLine=decl['endLine'])) + self.assertEqual(self.assess(graph)['contactStatus'], {status: 1}) + + def test_duplicate_declaration_identities_remain_ambiguous(self): + graph = self.graph() + duplicate = dict(graph['nodes'][1], id='other-field') + graph['nodes'].append(duplicate) + result = self.assess(graph) + self.assertEqual(result['contactStatus'], {'ambiguous_target': 1}) + self.assertEqual(result['summary']['field']['exactTargetAndOwner'], 0) + + def test_missing_anchor_is_retained_using_owner_file_only_for_scope(self): + graph = self.graph() + del graph['links'][0]['relationshipSite'] + self.assertEqual(self.assess(graph)['contactStatus'], {'unanchored': 1}) + + def test_graphify_line_contact_is_not_upgraded_to_exact_span(self): + graph = dict(directed=False, nodes=[ + dict(id='method', label='.read()', _callable=True, source_file='BindingFixture.java', source_location='L11'), + dict(id='field', label='left', source_file='BindingFixture.java', source_location='L6')], + links=[dict(source='method', target='field', relation='references', source_file='BindingFixture.java', source_location='L11')]) + result = self.assess(graph, 'graphify') + self.assertEqual(result['contactStatus'], {'unanchored': 1}) + self.assertEqual(result['summary']['field']['exactTargetAndOwner'], 0) + graph['links'][0].update(source='field', target='method') + self.assertEqual(self.assess(graph, 'graphify')['contactStatus'], {'unanchored': 1}) + + def test_duplicate_graph_ids_and_dangling_endpoints_fail(self): + for mutation in [lambda g: g['nodes'].append(g['nodes'][0]), + lambda g: g['links'][0].update(target='absent')]: + graph = self.graph() + mutation(graph) + with self.assertRaises(ValueError): + self.assess(graph) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 7564b0cb3..a70adf91f 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -3062,6 +3062,83 @@ registrations and baseline reports are preserved. Broader target precision, authored explanations, longer walks, actual god-object judgments and held-out confirmation remain open. +## Real Java source-binding census: independent field precision + +The earlier 3,896-record jsoup check established occurrence/endpoint consistency, +not semantic target precision. Registration `2499e927` now fixes all **88 Java 8 +base-source files** (1,150,637 bytes, including examples) before compiler binding +capture. It uses the existing pinned jsoup source and both frozen native graphs. +This is a complete census of that registered source set on a previously observed +development repository, not held-out or cross-language generalization. Java 11 +overlays, separately compiled package metadata and tests remain outside this +build configuration. + +A standalone auditor uses the public JDK +[JavacTask parse/analyze API](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.compiler/com/sun/source/util/JavacTask.html) +and source-tree bindings. Its +[source positions](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.compiler/com/sun/source/util/SourcePositions.html) +are converted from Java UTF-16 positions to exact UTF-8 offsets. Corretto +17.0.8.1 runs with `--release 8`, disabled processors, no implicit compilation, +matched cached dependency hashes and no project code generation/execution. +Compiler errors or incomplete/unbounded captures cannot produce a scored oracle. +No dependency is added to Compass execution or native tests. + +The compiler resolves 4,314 internal source references, plus 44 external fields, +55 array-length operations and 31 class literals. javac exposes the latter two +as field-kind elements; round 01 grouped them with non-source targets. Round 02 +retains them as separate intrinsic categories. That correction does not change +the internal denominator or either tool's results. Both rounds are preserved. + +| Source-declared evidence | Compass | Graphify | +| --- | ---: | ---: | +| Ordinary field declarations represented uniquely | 614/616 | 0/616 | +| Ordinary field references with exact target, owner and occurrence | 3,047/3,785 | 0/3,785 | +| Enum-constant declarations represented uniquely | 131/131 | 131/131 | +| Enum-constant references with exact target, owner and occurrence | 0/529 | 0/529 | +| Returned in-scope field contacts agreeing with the compiler | 3,047/3,047 | No contacts; precision unavailable | + +Every in-scope graph `references`/`reads`/`writes` contact to a field or enum +member is inventoried; the scorer retains wrong, unmapped, ambiguous and +unanchored records. All 3,047 Compass contacts match the compiler's source field +declaration, enclosing source owner and exact token span. No missing/ambiguous +identity or anchor failure occurs among these contacts. Graphify has no such +contacts, so an empty result cannot establish its positive precision. The +adapter preserves Graphify's undirected flag and never upgrades a line-only +record into an exact occurrence or directed use. Source-region identity is +checked; this is not a separate audit of every rendered qualified-name string. + +Compass still misses **738 ordinary field references and all 529 enum-constant +references**. Frequent missed targets include `TreeBuilder.stack` (51 uses), +`TokeniserState.nullChar` (47), `TokeniserState.eof` (41), and +`Parser.NamespaceHtml` (32). Source inspection confirms examples of inherited +state (`HtmlTreeBuilder` reading `TreeBuilder.stack`), statically imported +constants (`Jsoup.clean` reading `SharedConstants.DummyUri`), and unrepresented +anonymous-class fields (`expectedSize` and `i` in `Attributes.iterator`). These +examples do not classify every missing case. Missing contacts cannot establish +independent responsibilities or justify a god-object diagnosis. + +Qualification checks the source oracle against all 44 previously registered +scope cases and 45 independently bytecode-checked occurrences. An additional +20-record fixture covers two overloads, field/class initializers, lambdas, +anonymous/local classes, hidden fields, compound uses, folded constants, +external fields, array/class intrinsics, comments and Unicode. Unicode-escaped +identifier anchors remain explicit unsupported cases instead of guessed spans. +Fourteen new tests reject corruption, source drift, wrong targets/owners, +ambiguous identities, missing anchors and inappropriate Graphify span/direction +credit. All **176 benchmark tests and the product boundary pass**. Final whole-source compiler captures +are byte-identical, and both saved reviews replay exactly. + +`java_real_field_review.json` contains summaries, missing-field identities, +frequent missed targets and provenance hashes. Full raw bindings, every returned +contact judgment and all missing occurrences are retained under external +`jsoup-java-field-oracle-02`; `java_field_capture.py` and +`java_source_fields.py` provide reproducible capture and replay. The product +remains at `f2cacfeb`, version 0.3.30; this checkpoint changes only the auditor, +fixtures and documentation. Native Rust, JavaScript, platform and packaging +checks were not rerun. This strengthens semantic precision evidence for one +Java source configuration; authored explanations, longer walks, functional +communities, actual god-object judgments and held-out confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 35cb4aee13dafe754a55d0de6f98bc9f99ec0ee5 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:15:08 -0700 Subject: [PATCH 90/97] audit: register paired explanation source budget sweep --- .../explanation_budget_registration.json | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 benchmarks/agent_query/explanation_budget_registration.json diff --git a/benchmarks/agent_query/explanation_budget_registration.json b/benchmarks/agent_query/explanation_budget_registration.json new file mode 100644 index 000000000..4b1f7398f --- /dev/null +++ b/benchmarks/agent_query/explanation_budget_registration.json @@ -0,0 +1,67 @@ +{ + "schema": "compass.explanation-budget-registration/1", + "baselineCommit": "7b9d46f17f876c4c1414b4c08cdda80aaa07b82c", + "scope": "Previously inspected five-language development panel; budget sensitivity and latest-graph replay, not held-out evidence or authored-answer scoring.", + "sourceQuestions": "benchmarks/agent_query/responsibility_questions_panel_a.json", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "tasks": [ + { + "repository": "chi", + "symbol": "Mux", + "file": "mux.go", + "startLine": 21, + "kind": "struct" + }, + { + "repository": "click", + "symbol": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 455, + "kind": "class" + }, + { + "repository": "jsoup", + "symbol": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43, + "kind": "class" + }, + { + "repository": "redux", + "symbol": "createStore", + "file": "src/createStore.ts", + "startLine": 86, + "kind": "function" + }, + { + "repository": "walkdir", + "symbol": "IntoIter", + "file": "src/lib.rs", + "startLine": 566, + "kind": "struct" + } + ], + "graphRun": "java-state-access-02/run.json", + "graphRunSha256": "c315bffe56cbb7a7684c088e0219fc85ba9ef0681a39463608845691522d21ff", + "binary": "java-state-access-02/compass", + "binarySha256": "5f6ff5f9b7b13866fdc7435e7a05888d3a95a0b23636be89cf45bc3478660139", + "priorCapture": "exact-symbol-08/capture/capture.json", + "priorCaptureSha256": "4263f8d01ace8a5d7caa3595642115875bdd91c63e5b3a3d03bd12c3abd3c91a", + "sourceBudgets": [ + 2000, + 4000, + 8000, + 16000, + 32000 + ], + "identityControl": "Both tools receive the same known symbol, repository-relative file, declaration line and expected kind. Compass uses search_symbols exact=true, source_file, start_line, kind with existing limits (256 candidates, 500 nodes, 524288 response bytes). Graphify uses its existing file::symbol get_node; validate returned source identity and source-declaration kind afterward. Do not imply Graphify natively accepts all filters. One call per tool/subject, no retries. Preserve all ambiguity/error/limit outcomes.", + "retrieval": "One public exact resolver and one public get_neighbors request per tool/subject. Membership anchors come only from outgoing contains/method rows in that response. Group equal file/line anchors, sort by file and line, read from each anchor to the next returned anchor in its file; final window ends at min(EOF, start+4096). Cap concatenated raw source bytes by each registered budget. No question, witness, graph-file, name-ranking or source-content selection. Same policy for both tools. No extra calls or retries to improve a score.", + "scoring": "Preserve all 20 facts at every budget. Report literal witness coverage and the prior fixed semantic allowance for click-1 (only class header missing, source identity verified and all other witness lines present). No partial-fact credit. Missing resolver, source, truncation or tool error remains explicit and cannot become a successful empty inventory. Raw source is evidence, never an authored answer.", + "verification": "Check source pins, clean source checkouts, source/graph/binary/package hashes before and after capture; preserve raw MCP calls and bytes. Independently replay window byte offsets and source hashes. Compare the 8000-byte results with the old capture, report every payload/anchor/window change. Publish all five budgets, including ties and losses; retain the historical scores.", + "limitations": [ + "Known development questions and prior misses have been read before choosing these geometric budgets.", + "Equal retained source quotas are not equal compute, context tokens or graph-payload budgets. Report actual source and response bytes.", + "A larger source quota is not a product improvement; no product changes or superior explanation claim follow from this experiment.", + "Full question coverage, semantic correctness, authored answers, god-object judgments and held-out quality remain separate requirements." + ] +} From e445c5c6891432c30771a316c48d7ebda331c3ef Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:24:05 -0700 Subject: [PATCH 91/97] audit: measure paired explanation source budget sensitivity --- benchmarks/agent_query/README.md | 25 + .../explanation_budget_review.json | 1536 +++++++++++++++++ benchmarks/agent_query/source_windows.py | 151 ++ .../agent_query/tests/test_source_windows.py | 111 ++ ...ode-graph-intelligence-audit-2026-09-26.md | 79 + 5 files changed, 1902 insertions(+) create mode 100644 benchmarks/agent_query/explanation_budget_review.json create mode 100644 benchmarks/agent_query/source_windows.py create mode 100644 benchmarks/agent_query/tests/test_source_windows.py diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 2b07325cd..9346e35af 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -658,3 +658,28 @@ round 01's mixed non-source category remains explicitly superseded. These result strengthen source-declaration precision evidence for this configuration; they do not score read/write effects, explanations, paths, community quality or actual god-object defects. + +### Explanation source-budget sensitivity + +`explanation_budget_registration.json` freezes the existing five repositories, +20 facts, exact identity constraints, and five source quotas before capture. +`explanation_budget_review.json` publishes every result and actual byte cost. +The latest graphs still score 14/20 versus 15/20 at 8,000 bytes. At 16,000 the +scores are 19/20 versus 18/20; at 32,000 they are 20/20 versus 18/20. Both tie +8/20 at 2,000 and 4,000. These measure source evidence, not authored answers or +overall superiority; larger quotas also have unequal actual source usage. + +`source_windows.py` plans bounded windows solely from public membership anchors +and scores the unchanged witnesses. It rejects missing anchors and unsafe paths, +retains clipped raw bytes, and applies the historical Click header allowance only +with verified identity. All 187 benchmark tests pass, including 11 new tests: + +```sh +python3 -m unittest benchmarks.agent_query.tests.test_source_windows +``` + +External `explanation-budget-01` contains the collector, independent verifier, +20 public-call transcripts, 50 source-window arms and payload deltas. All ten +8,000-byte source controls reproduce the old bytes and outcomes. The audit +explains source-order starvation, Graphify's two Redux interval gaps, and the +much larger Compass graph payloads. No production code changes in this arm. diff --git a/benchmarks/agent_query/explanation_budget_review.json b/benchmarks/agent_query/explanation_budget_review.json new file mode 100644 index 000000000..681bf81b3 --- /dev/null +++ b/benchmarks/agent_query/explanation_budget_review.json @@ -0,0 +1,1536 @@ +{ + "schema": "compass.explanation-budget-review/1", + "registration": "benchmarks/agent_query/explanation_budget_registration.json", + "registrationSha256": "7c59d1338ee3076028d2f32934c22a921786e6eec8cd392658ceb2eefcd37d52", + "registrationCommit": "35cb4aee", + "productCommit": "f2cacfebba15d69a738e7c14694d51320e4e07ba", + "evaluatedBinarySha256": "5f6ff5f9b7b13866fdc7435e7a05888d3a95a0b23636be89cf45bc3478660139", + "scope": "Previously inspected five-language development panel; budget sensitivity and latest-graph replay, not held-out evidence or authored-answer scoring.", + "summary": [ + { + "budget": 2000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 10000 + }, + { + "budget": 2000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 10000 + }, + { + "budget": 4000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 19673 + }, + { + "budget": 4000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 19673 + }, + { + "budget": 8000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 14, + "literalWitnessFacts": 13, + "sourceBytes": 35673 + }, + { + "budget": 8000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 15, + "literalWitnessFacts": 14, + "sourceBytes": 35673 + }, + { + "budget": 16000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 19, + "literalWitnessFacts": 18, + "sourceBytes": 59542 + }, + { + "budget": 16000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53417 + }, + { + "budget": 32000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 20, + "literalWitnessFacts": 19, + "sourceBytes": 64644 + }, + { + "budget": 32000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53747 + } + ], + "results": [ + { + "repository": "chi", + "tool": "compass", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "jsoup-3" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 16000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 32000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 16000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 32000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 16000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 32000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 16000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 32000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "walkdir-3" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 32000, + "sourceBytes": 21102, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "walkdir-2", + "walkdir-3" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 32000, + "sourceBytes": 16330, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + } + ], + "publicCalls": 20, + "publicCallCosts": { + "compass": { + "resolver": { + "textBytes": 3039, + "wireResponseBytes": 21983 + }, + "neighbors": { + "textBytes": 33061, + "wireResponseBytes": 372871 + } + }, + "graphify": { + "resolver": { + "textBytes": 619, + "wireResponseBytes": 1094 + }, + "neighbors": { + "textBytes": 6980, + "wireResponseBytes": 7534 + } + } + }, + "historicalComparisons": [ + { + "repository": "chi", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "chi", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "click", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "click", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "jsoup", + "tool": "compass", + "resolverResponseUnchanged": false, + "neighborResponseUnchanged": false, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "jsoup", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "redux", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "redux", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "walkdir", + "tool": "compass", + "resolverResponseUnchanged": false, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + }, + { + "repository": "walkdir", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true, + "windowBytesUnchanged": true, + "omittedGroupsUnchanged": true + } + ], + "verification": { + "publicCallsVerified": 20, + "windowArmsVerified": 50, + "membershipAnchorsVerified": 153, + "sourcePinsAndHashesBeforeAfter": true, + "unchanged8000ByteWindowArms": 10, + "previous8000ByteScoresReproduced": true, + "legacy8000BytePolicyReplayed": true, + "benchmarkTests": 187, + "newWindowTests": 11, + "productBoundaryPassed": true + }, + "findings": [ + "The unchanged 8000-byte workflow still yields Compass 14/20 versus Graphify 15/20; Java and Rust field improvements did not improve this membership-only retrieval policy.", + "Both tools tie 8/20 at 2000 and 4000 bytes. Compass reaches 19/20 at 16000 and 20/20 at 32000; Graphify reaches 18/20 at both larger quotas.", + "Compass still misses the WalkDir symlink-loop fact at 16000 bytes. Graphify lacks the Redux enhancer witness before its earliest returned membership anchor and the final store/observable witness beyond the fixed 4096-byte last window. These are limitations of the registered workflow, not proof the tools cannot retrieve that source using other operations.", + "Compass returns substantially larger graph payloads. Higher quota scores also consume more actual source bytes: 64644 versus 53747 at the largest per-subject quota.", + "All membership rows and 8000-byte windows are unchanged. Changed resolver payloads carry graph/view digests for jsoup and WalkDir; jsoup neighbor differences are community metadata and its corresponding serialized-byte requirement. See payload-deltas.json.", + "Full 20/20 witness support does not establish complete answers to the natural-language questions, semantic entailment of unseen callees, independent adjudication, god-object detection or overall superiority." + ], + "limitations": [ + "Known development questions and prior misses have been read before choosing these geometric budgets.", + "Equal retained source quotas are not equal compute, context tokens or graph-payload budgets. Report actual source and response bytes.", + "A larger source quota is not a product improvement; no product changes or superior explanation claim follow from this experiment.", + "Full question coverage, semantic correctness, authored answers, god-object judgments and held-out quality remain separate requirements.", + "The fixed Click class-header allowance contributes one non-literal fact to each tool at each budget; report literal totals alongside source-evidence totals.", + "Reading source between membership anchors can include comments or unrelated declarations. The last-window 4096-byte rule can impose a ceiling even when global quota remains.", + "The evaluator reads and validates all anchor files, including omitted anchors, before selecting windows. Equal retained-byte quotas are not equal disk reads.", + "No Rust, JavaScript, platform or packaging checks were rerun: this change is an audit harness and evidence update, with no product code change." + ], + "artifacts": { + "explanation-budget-01/benchmark-tests.log": "b961c66bc71e45d83631701c53491f62f2c63ac92b6203d5fe2e8abea16b173a", + "explanation-budget-01/capture/capture.json": "c2db3be974793b100dbb183eab35d474b0ed4d6d3c62f85aba6934c326738e0d", + "explanation-budget-01/capture/collect.py": "7c5e857a7f59a145438be7acba1e1df1aa181be2f96fe3f024eb01c64d8b5a65", + "explanation-budget-01/capture/community_identity.py": "5bc3d1c574211aa3180a2334fe6c758043f384651948b7077aba3ea879de4503", + "explanation-budget-01/capture/community_navigation.py": "808df1c7b15c8c2afc86ac1ed09dc4f339c98a77d7a7b14eb4866e3da1c8c0f8", + "explanation-budget-01/capture/community_tasks.py": "77f599ff7ee7eebfe4b590fee6c771ecfb3ed475d1944e37bcabc110634d3660", + "explanation-budget-01/capture/explanation_budget_registration.json": "7c59d1338ee3076028d2f32934c22a921786e6eec8cd392658ceb2eefcd37d52", + "explanation-budget-01/capture/graphify-mcp-environment.json": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535", + "explanation-budget-01/capture/mcp_compare.py": "d23f093a1300fcf825457f917694097f0f8b8dc8ee8d671946032f5e4e89a5dc", + "explanation-budget-01/capture/mcp_transport.py": "daaad69c554f824a1df94f0ef93cb9d4c6e9b4f4b712d1611323d524e10d44ee", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/compass/legacy-8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "explanation-budget-01/capture/raw/chi/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/chi/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "explanation-budget-01/capture/raw/chi/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/chi/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/chi/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "explanation-budget-01/capture/raw/chi/compass/mcp/04.request.json": "34232dc4cae123d91883f2efff5f57b566033c899dbfaa93e98c0050c7f62a3c", + "explanation-budget-01/capture/raw/chi/compass/mcp/04.response.jsonl": "7938a7e7afec02546e9bae682eefd8973e7dbccbd446fece89aecbfccdbbdb7d", + "explanation-budget-01/capture/raw/chi/compass/mcp/05.request.json": "350ecbf25523d5f934821418e2b92aa51e54ac60547d2f2ba2787b5aee29c14e", + "explanation-budget-01/capture/raw/chi/compass/mcp/05.response.jsonl": "09b3992280dc91e7c6a9203f46e1c8b994742d81bc4c2f179b3160c2adf3f08a", + "explanation-budget-01/capture/raw/chi/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "explanation-budget-01/capture/raw/chi/compass/windows/16000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "explanation-budget-01/capture/raw/chi/compass/windows/2000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/windows/2000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/windows/2000/002.source": "21c866f0d603e57248e2400aeaf96ff1167fac4310cac372c28986e727c32649", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "explanation-budget-01/capture/raw/chi/compass/windows/32000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/compass/windows/4000/010.source": "18c9e1ec72927bc57f70d271df8bd2a55e9b1dd6852595b02f63ce83430a4883", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/compass/windows/8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/graphify/legacy-8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "explanation-budget-01/capture/raw/chi/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/chi/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "explanation-budget-01/capture/raw/chi/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/chi/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/chi/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "explanation-budget-01/capture/raw/chi/graphify/mcp/04.request.json": "5212851af235eed2bd62a674fa14313ea9a38591a8d706897051badabc911735", + "explanation-budget-01/capture/raw/chi/graphify/mcp/04.response.jsonl": "d4d8b29632b3aae99cc57178a3b6ccb52935ae78e6848569b08c64f60b5a75a8", + "explanation-budget-01/capture/raw/chi/graphify/mcp/05.request.json": "43b918f02295bb3b8b86531c0b4f139acb79116d408969e2655c76a0601119e3", + "explanation-budget-01/capture/raw/chi/graphify/mcp/05.response.jsonl": "43678e134e41a496da5198713dbc822810d661c9644a5b50fda48dfa6e13b552", + "explanation-budget-01/capture/raw/chi/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "explanation-budget-01/capture/raw/chi/graphify/windows/16000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "explanation-budget-01/capture/raw/chi/graphify/windows/2000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/windows/2000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/windows/2000/002.source": "21c866f0d603e57248e2400aeaf96ff1167fac4310cac372c28986e727c32649", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "explanation-budget-01/capture/raw/chi/graphify/windows/32000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/graphify/windows/4000/010.source": "18c9e1ec72927bc57f70d271df8bd2a55e9b1dd6852595b02f63ce83430a4883", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "explanation-budget-01/capture/raw/chi/graphify/windows/8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/legacy-8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/click/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "explanation-budget-01/capture/raw/click/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/click/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/click/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "explanation-budget-01/capture/raw/click/compass/mcp/04.request.json": "e148a7f5170d1a8363e3cbaa97110c53950669de602afcc5ef3fd30d2c60c770", + "explanation-budget-01/capture/raw/click/compass/mcp/04.response.jsonl": "b246a63c4f33e46eed5f15e5789db1bfa54e6ea0eb63c4759a5c4462426d4b21", + "explanation-budget-01/capture/raw/click/compass/mcp/05.request.json": "4c0ad7366492176ab7c2dd6510655cb151eead3831c1217eb92772f9d1eb4bb2", + "explanation-budget-01/capture/raw/click/compass/mcp/05.response.jsonl": "b1cfb966e35d68905b7d7f888a689d58a57d162d738d49355b58e7ec0f1a7eff", + "explanation-budget-01/capture/raw/click/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/click/compass/windows/16000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/windows/16000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/windows/16000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/windows/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/windows/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/windows/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/windows/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/compass/windows/2000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/windows/2000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/windows/2000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/windows/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/windows/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/windows/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/windows/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "explanation-budget-01/capture/raw/click/compass/windows/32000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/windows/32000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/windows/32000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/windows/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/windows/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/windows/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/windows/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/compass/windows/4000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/windows/4000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/windows/4000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/windows/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/windows/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/windows/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/windows/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/compass/windows/8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/compass/windows/8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/compass/windows/8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/compass/windows/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/compass/windows/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/compass/windows/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/compass/windows/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/legacy-8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/click/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "explanation-budget-01/capture/raw/click/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/click/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/click/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "explanation-budget-01/capture/raw/click/graphify/mcp/04.request.json": "4aad9fe4bfc910ea3b74fa2a6bb4335c66ede5da3d24cf48609142507b61a000", + "explanation-budget-01/capture/raw/click/graphify/mcp/04.response.jsonl": "d22730f840576483c14ecb89c933817acf713e534c6a2b8b8a69ecc7e98714a2", + "explanation-budget-01/capture/raw/click/graphify/mcp/05.request.json": "faea67c086ba783dd80230e4b0f019e3c07bfef93a31ed288f0f2cec10cc0c03", + "explanation-budget-01/capture/raw/click/graphify/mcp/05.response.jsonl": "b98212f878f04290fcd5c68ab617bd1cff708c7ba1b23dd9d277a584b595bb72", + "explanation-budget-01/capture/raw/click/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/windows/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/windows/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/windows/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/windows/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "explanation-budget-01/capture/raw/click/graphify/windows/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/compass/legacy-8000/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/04.request.json": "88321b0cefc99d73f5f5bab430106fa357e7ce37ea60d7273e573f6caf186d36", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/04.response.jsonl": "c88e7e11ddfca95460f1ba73fd91d2ff376914106db214ad87cc62253d4410fc", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/05.request.json": "ff3fcdd774e4493d27f81784e37e91f956cbecc7f07e434c96ec212f96bf2280", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/05.response.jsonl": "875ee5125b384d6f9e66d56e231c491c8959d95014b256f08253ea5a8e483b0a", + "explanation-budget-01/capture/raw/jsoup/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "explanation-budget-01/capture/raw/jsoup/compass/windows/16000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "explanation-budget-01/capture/raw/jsoup/compass/windows/2000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/windows/2000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/windows/2000/002.source": "f001d1fcca3d1fcb243f116312615cc01d807a28d05dfc3cdf4b8b0287ebc490", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "explanation-budget-01/capture/raw/jsoup/compass/windows/32000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "explanation-budget-01/capture/raw/jsoup/compass/windows/4000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/windows/4000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/windows/4000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/compass/windows/4000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/compass/windows/4000/004.source": "426592e2fb2b4d671f607a34bc6d5eedfe454775ce3304674af04738bdd220a2", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/compass/windows/8000/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/graphify/legacy-8000/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/04.request.json": "883da0706262e806f7254d1763f680dbebb7917ab916867d30c4f43b2ae6b8b9", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/04.response.jsonl": "7ac5d473f020a3eef7e5f7aeb277fa5b33784b888f2d83cdb791ee569b66ec11", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/05.request.json": "722d113b0a110540d31515b29f0c07c5ef96f04d5bf471ffb26605db2cd493ff", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/05.response.jsonl": "b3f990a6d8fc01f0d15ca9272e7c95e8ad376f2a4dae010105662903f08d3896", + "explanation-budget-01/capture/raw/jsoup/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/16000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/2000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/2000/001.source": "88dc87722aeb25b94e9d65aca2fba435de9549e5b610468138f69fad1007f03b", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/32000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/4000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/4000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/4000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/4000/003.source": "92f42d96bb1e12fff294e506bf96320736381f55b60044a2e0fcaa394d7a7e66", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "explanation-budget-01/capture/raw/jsoup/graphify/windows/8000/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "explanation-budget-01/capture/raw/redux/compass/legacy-8000/015.source": "2fa50459f3adaa2c60d3d328d13ddb749207e27feb3c92fe43812e9f2edc01c6", + "explanation-budget-01/capture/raw/redux/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/redux/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "explanation-budget-01/capture/raw/redux/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/redux/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/redux/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "explanation-budget-01/capture/raw/redux/compass/mcp/04.request.json": "8803daedf87b9bd35456accb3487cc9e3491a392638e95b56a3d55f8e66f89d5", + "explanation-budget-01/capture/raw/redux/compass/mcp/04.response.jsonl": "f44f175ad81c0379dd06e156a504fb1f3ceb8e31bd4ddf62777feabdc0700b8c", + "explanation-budget-01/capture/raw/redux/compass/mcp/05.request.json": "f14c1893f73d7dc8de473fa0cf98d94ca628c56f486b2eb9f9d3f062f42fc479", + "explanation-budget-01/capture/raw/redux/compass/mcp/05.response.jsonl": "52c6b1012ea90597e06714535dacfd36b80c47132d41501ec6db928751620cc0", + "explanation-budget-01/capture/raw/redux/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/015.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/016.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/017.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/018.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/019.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/020.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/021.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "explanation-budget-01/capture/raw/redux/compass/windows/16000/022.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/windows/2000/011.source": "1bb164faae3ccfe7de42a98c03dbd69039a01c9cd6741b48f9b017d0d583ddac", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/015.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/016.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/017.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/018.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/019.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/020.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/021.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "explanation-budget-01/capture/raw/redux/compass/windows/32000/022.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "explanation-budget-01/capture/raw/redux/compass/windows/4000/014.source": "f991a26b534be8084f3b1bb9067c7031357eb3c52bc9adcb9f504010fb133047", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "explanation-budget-01/capture/raw/redux/compass/windows/8000/015.source": "2fa50459f3adaa2c60d3d328d13ddb749207e27feb3c92fe43812e9f2edc01c6", + "explanation-budget-01/capture/raw/redux/graphify/legacy-8000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "explanation-budget-01/capture/raw/redux/graphify/legacy-8000/001.source": "104842ff0af72b77928d0a849bc5aa5e5bf080e95aeca8f8bc0baacd3dd90aa1", + "explanation-budget-01/capture/raw/redux/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/redux/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "explanation-budget-01/capture/raw/redux/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/redux/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/redux/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "explanation-budget-01/capture/raw/redux/graphify/mcp/04.request.json": "c32cfb2450c017920145bae06b04af6f131c02499e30d987f76daf8e53f1a5e9", + "explanation-budget-01/capture/raw/redux/graphify/mcp/04.response.jsonl": "cba36e66dcc88f9313662b40b072d5c24cf9fbf25310ac3b1c2f4eaebc8e02c6", + "explanation-budget-01/capture/raw/redux/graphify/mcp/05.request.json": "f38f6d67ccd3f8f0281ba3709abeaab237da52011550ad8bd0bd1a9fa035a6b1", + "explanation-budget-01/capture/raw/redux/graphify/mcp/05.response.jsonl": "0ec3d11ad7c82aab1979ae0fe8cd9dc1622478bc3a24e0898627e61b9636769b", + "explanation-budget-01/capture/raw/redux/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/redux/graphify/windows/16000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "explanation-budget-01/capture/raw/redux/graphify/windows/16000/001.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "explanation-budget-01/capture/raw/redux/graphify/windows/2000/000.source": "718759fe6d1d524a045503d49b4d6af5791f62227270ed358a0c8efd1cb2c0e8", + "explanation-budget-01/capture/raw/redux/graphify/windows/32000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "explanation-budget-01/capture/raw/redux/graphify/windows/32000/001.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "explanation-budget-01/capture/raw/redux/graphify/windows/4000/000.source": "6ac89178efb4169df169a238447bcdb738c5a8f6d926212dded707cedc73ea22", + "explanation-budget-01/capture/raw/redux/graphify/windows/8000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "explanation-budget-01/capture/raw/redux/graphify/windows/8000/001.source": "104842ff0af72b77928d0a849bc5aa5e5bf080e95aeca8f8bc0baacd3dd90aa1", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "explanation-budget-01/capture/raw/walkdir/compass/legacy-8000/009.source": "907ef6bd190a9985da364ec084579cacae6b06067d5a3bdfa8d98a2c3256eb9b", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/04.request.json": "d339f2ef2ad14a8ba3ccfaeed06102dae89e51ee863ddda562fcef92e1262bd2", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/04.response.jsonl": "28624be871949873b0f55b789ce3549ba5c966eeca09c89ccb526adc0607fa00", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/05.request.json": "75e03f39e5544dca970a2ed6183ee7389eaab84a0edf9b41c9ed3aead0a0b2f5", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/05.response.jsonl": "2b14975c56bdfab6f4d8926511462d5603fc9b83dfffe030cbdb8e6c3b11610f", + "explanation-budget-01/capture/raw/walkdir/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/009.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/010.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/011.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/012.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/013.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/014.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/015.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "explanation-budget-01/capture/raw/walkdir/compass/windows/16000/016.source": "6e64bcf58026de82970666d4b4db45e862854dac48782a6034087bc047263137", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/windows/2000/007.source": "cbbf1bd40fef95750f8722ee53276dca4d5289613e8082eb030a14236d235ed0", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/009.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/010.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/011.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/012.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/013.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/014.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/015.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/016.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/017.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/018.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "explanation-budget-01/capture/raw/walkdir/compass/windows/32000/019.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/windows/4000/007.source": "b01050c7b77958949f0ba035e4783dc6e44688bd1937800531564e275cfb29f6", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "explanation-budget-01/capture/raw/walkdir/compass/windows/8000/009.source": "907ef6bd190a9985da364ec084579cacae6b06067d5a3bdfa8d98a2c3256eb9b", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/graphify/legacy-8000/005.source": "b0c0b5e23f7d4cf98d355ff7f08101ea772f51b09b278f51ab7bb66ad2aaf4a6", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/04.request.json": "ac9506fc18f6f6cf468a46a9e781188b01156b4b7c6ab64ad601ab06640a8c25", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/04.response.jsonl": "2190ecdb65f6a8ba2da85c820eccc0fdd4f5c647cedd341f1d624e1368c0977e", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/05.request.json": "18b4ad53dd232900c7db7d584dd42b068d0d3e27cd663c34c4433b6ad5b89197", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/05.response.jsonl": "ea3873a259a5818f1f61c5cef724df012ca38927954973acd3c2e8ff860c2585", + "explanation-budget-01/capture/raw/walkdir/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/005.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/006.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/007.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/008.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/16000/010.source": "6200f361738587d8826b72cc779afd1b8d11b135feaa3b98377ead3d14218764", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/2000/000.source": "e9afda558aa648a1626f292d9445290945caf90d6eaf7af24e1a9f32e032118a", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/005.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/006.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/007.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/008.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/32000/010.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/4000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/4000/001.source": "fb4eb23be72cc1c60225692eb7bf38b91a4aea2f2b830a4ae4c9063b853f37cf", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "explanation-budget-01/capture/raw/walkdir/graphify/windows/8000/005.source": "b0c0b5e23f7d4cf98d355ff7f08101ea772f51b09b278f51ab7bb66ad2aaf4a6", + "explanation-budget-01/capture/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "explanation-budget-01/capture/source_windows.py": "019561cb6849e4e38f62b0322a1f6a65d73c6f32f15955c1e371c4925e38cf94", + "explanation-budget-01/capture/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "explanation-budget-01/capture.log": "cd264ee9554666a37bfc20ab8cc45e85980978732d3ca1a9446beb304576538f", + "explanation-budget-01/collect.py": "7c5e857a7f59a145438be7acba1e1df1aa181be2f96fe3f024eb01c64d8b5a65", + "explanation-budget-01/payload-category-check.json": "0d139801d76ba0c462e832238999c52525659bcc0fbce847321f88419f1b38b7", + "explanation-budget-01/payload-deltas.json": "89264b6bd67fcd64cb7bafd41645f624a2ca12c6ac2b0c86615a9c303b7dcbc4", + "explanation-budget-01/product-boundary.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "explanation-budget-01/repeat-verification.log": "0e160a6f599cbcece7c6754fe654b81d0fbbfabb2572020821d2b3e6a006c0cf", + "explanation-budget-01/replay.json": "92cdfeaf21d781cf956f73a9e9eb604a7ed62a6e302b436effccda759a5b8826", + "explanation-budget-01/verification.log": "0e160a6f599cbcece7c6754fe654b81d0fbbfabb2572020821d2b3e6a006c0cf", + "explanation-budget-01/verified-summary.json": "12530435c1ea5c21d0dc93e540868b20cee37f06b295864d568cae9060d97c3f", + "explanation-budget-01/verify.py": "40123dee617836b5ecdd6fa4f1503ee2e38d95a08381d288065623c371385495", + "explanation-budget-01/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "explanation-budget-01/write_review.py": "4f7c7a11d135ca343104022d48a34f859b5af12bec7bb512bf1abda38dd99fd7" + } +} diff --git a/benchmarks/agent_query/source_windows.py b/benchmarks/agent_query/source_windows.py new file mode 100644 index 000000000..c6b7104da --- /dev/null +++ b/benchmarks/agent_query/source_windows.py @@ -0,0 +1,151 @@ +"""Symmetric source-window control; anchors only, without oracle-based selection. + +This is an evaluation policy, not a production explanation implementation. +Byte intervals are authoritative, including cuts through a UTF-8 code point. +""" +from collections import defaultdict +import hashlib +from pathlib import Path + +from benchmarks.agent_query.community_tasks import read_bounded + +MAX_ROWS = 10000 +MAX_FILE_BYTES = 4194304 +MAX_FILES = 128 +MAX_TOTAL_FILE_BYTES = 16777216 + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def source_windows(root, rows, budget): + if type(budget) is not int or not 1 <= budget <= 1048576: + raise ValueError('source budget must be an integer from 1 to 1048576') + if not isinstance(rows, list) or len(rows) > MAX_ROWS: + raise ValueError('membership row limit exceeded') + root = Path(root).resolve() + groups = defaultdict(list) + for row in rows: + file, line = row.get('file'), row.get('line') + if not isinstance(file, str) or not file or type(line) is not int or line < 1: + raise ValueError('missing or invalid membership source anchor') + relative = Path(file) + if relative.is_absolute() or '..' in relative.parts: + raise ValueError('unsafe membership source path') + groups[file, line].append(row) + files = {} + offsets = {} + total = 0 + # Validate every anchor, including anchors beyond the retained-byte budget. + for file, line in sorted(groups): + if file not in files: + if len(files) >= MAX_FILES: + raise ValueError('source file count limit exceeded') + path = (root / file).resolve() + if not path.is_relative_to(root): + raise ValueError('source path escapes root') + data = read_bounded(path, MAX_FILE_BYTES) + total += len(data) + if total > MAX_TOTAL_FILE_BYTES: + raise ValueError('aggregate source read limit exceeded') + files[file] = data + starts = [0] + for text in data.splitlines(keepends=True): + starts.append(starts[-1] + len(text)) + offsets[file] = starts + if line >= len(offsets[file]): + raise ValueError('membership line outside source') + keys = sorted(groups) + windows = [] + remaining = budget + for index, (file, line) in enumerate(keys): + if not remaining: + break + data = files[file] + start = offsets[file][line - 1] + following = keys[index + 1] if index + 1 < len(keys) else None + end = (offsets[file][following[1] - 1] if following and following[0] == file + else min(len(data), start + 4096)) + kept_end = min(end, start + remaining) + part = data[start:kept_end] + windows.append(dict(file=file, startLine=line, startByte=start, endByte=kept_end, + requestedEndByte=end, partial=kept_end < end, + sourceBytes=len(part), sourceSha256=sha(part), + fileSha256=sha(data), members=groups[file, line])) + remaining -= len(part) + for file, data in files.items(): + if read_bounded(root / file, MAX_FILE_BYTES) != data: + raise ValueError('source changed during window planning') + return dict(budget=budget, sourceBytes=budget - remaining, windows=windows, + omittedGroups=[dict(file=f, line=n) for f, n in keys[len(windows):]], + sourceReadBytes=total, missingAnchorRows=[]) + + +def score_windows(root, case, control, *, identity_verified=False): + """Verify saved intervals and witness text before scoring all-or-nothing facts.""" + root = Path(root).resolve() + file = case['file'] + relative = Path(file) + path = (root / relative).resolve() + if relative.is_absolute() or '..' in relative.parts or not path.is_relative_to(root): + raise ValueError('unsafe question source path') + data = read_bounded(path, MAX_FILE_BYTES) + if sha(data) != case['sourceFileSha256']: + raise ValueError('question source hash mismatch') + source_lines = data.decode('utf-8').splitlines() + returned = {} + total = 0 + for w in control['windows']: + rel = Path(w['file']) + p = (root / rel).resolve() + if rel.is_absolute() or '..' in rel.parts or not p.is_relative_to(root): + raise ValueError('unsafe window source path') + raw = read_bounded(p, MAX_FILE_BYTES) + start, end = w['startByte'], w['endByte'] + if (type(start) is not int or type(end) is not int + or not 0 <= start <= end <= len(raw)): + raise ValueError('invalid window interval') + part = raw[start:end] + if sha(raw) != w['fileSha256'] or sha(part) != w['sourceSha256']: + raise ValueError('window hash mismatch') + if len(part) != w['sourceBytes']: + raise ValueError('window byte count mismatch') + starts = [0] + for text in raw.splitlines(keepends=True): + starts.append(starts[-1] + len(text)) + line = w['startLine'] + if type(line) is not int or not 1 <= line < len(starts) or starts[line - 1] != start: + raise ValueError('window line and byte disagree') + total += len(part) + if w['file'] == file: + for offset, text in enumerate(part.decode('utf-8', errors='replace').splitlines()): + # A clipped sequence is retained as replacement text, never padded. + number = line + offset + if number in returned: + raise ValueError('overlapping source windows') + returned[number] = text + if total != control['sourceBytes'] or total > control['budget']: + raise ValueError('source budget accounting mismatch') + judgments = [] + for fact in case['facts']: + missing = [] + literal = True + for witness in fact['witnesses']: + first, last = witness['startLine'], witness['endLine'] + if '\n'.join(source_lines[first - 1:last]).strip() != witness['text'].strip(): + raise ValueError('witness differs from pinned source') + for index, expected in enumerate(witness['text'].splitlines(), first): + actual = returned.get(index) + literal &= actual == expected + if actual is None or actual.strip() != expected.strip(): + missing.append(dict(line=index, expected=expected, returned=actual)) + # Fixed historical allowance, not a general missing-header rule. + allowance = (identity_verified and fact['id'] == 'click-1' and len(missing) == 1 + and missing[0]['line'] == 455 + and missing[0]['expected'] == 'class _AtomicFile:') + judgments.append(dict(fact=fact['id'], literalWitnessCoverage=literal, + sufficientSourceEvidence=not missing or allowance, + classHeaderAllowance=allowance, + missingAfterIndentNormalization=missing)) + return judgments diff --git a/benchmarks/agent_query/tests/test_source_windows.py b/benchmarks/agent_query/tests/test_source_windows.py new file mode 100644 index 000000000..c7d81ee48 --- /dev/null +++ b/benchmarks/agent_query/tests/test_source_windows.py @@ -0,0 +1,111 @@ +import copy +from pathlib import Path +import tempfile +import unittest + +from benchmarks.agent_query.source_windows import score_windows, sha, source_windows + + +class SourceWindowTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.data = b'first\nsecond\nthird\nfourth\n' + (self.root / 'x').write_bytes(self.data) + self.rows = [dict(file='x', line=1), dict(file='x', line=3)] + + def case(self, text='second', first=2, last=2): + return dict(file='x', sourceFileSha256=sha(self.data), facts=[dict( + id='test', witnesses=[dict(startLine=first, endLine=last, text=text)])]) + + def test_order_grouping_and_shared_raw_byte_budget(self): + rows = [self.rows[1], self.rows[0], dict(self.rows[0], label='duplicate')] + r = source_windows(self.root, rows, 16) + self.assertEqual(r['sourceBytes'], 16) + self.assertEqual([(w['startByte'], w['endByte']) for w in r['windows']], [(0, 13), (13, 16)]) + self.assertEqual(len(r['windows'][0]['members']), 2) + self.assertTrue(r['windows'][1]['partial']) + self.assertEqual(r['sourceReadBytes'], len(self.data)) + + def test_quota_exhaustion_retains_omissions(self): + r = source_windows(self.root, self.rows, 6) + self.assertEqual(r['omittedGroups'], [dict(file='x', line=3)]) + self.assertFalse(score_windows(self.root, self.case(), r)[0]['sufficientSourceEvidence']) + + def test_last_window_has_4096_byte_cap(self): + (self.root / 'x').write_bytes(b'a' * 5000) + r = source_windows(self.root, [self.rows[0]], 32000) + self.assertEqual(r['sourceBytes'], 4096) + self.assertFalse(r['windows'][0]['partial']) + + def test_missing_and_invalid_anchors_fail_even_after_budget(self): + for bad in [dict(file='x', line=100), dict(file='x', line=True), + dict(file='x'), dict(file='../x', line=1), dict(file='/x', line=1)]: + with self.subTest(bad=bad), self.assertRaises(ValueError): + source_windows(self.root, self.rows + [bad], 1) + for budget in [True, 0, -1, 1048577]: + with self.assertRaises(ValueError): + source_windows(self.root, self.rows, budget) + + def test_symlink_escape_fails(self): + with tempfile.TemporaryDirectory() as other: + target = Path(other) / 'outside' + target.write_bytes(b'outside') + (self.root / 'link').symlink_to(target) + with self.assertRaises(ValueError): + source_windows(self.root, [dict(file='link', line=1)], 10) + + def test_fact_requires_all_witness_lines(self): + for budget, expected in [(11, False), (13, True)]: + r = source_windows(self.root, self.rows, budget) + judgment = score_windows(self.root, self.case(), r)[0] + self.assertEqual(judgment['sufficientSourceEvidence'], expected) + + def test_utf8_cut_is_retained_without_inventing_complete_character(self): + self.data = '🧭\n'.encode() + (self.root / 'x').write_bytes(self.data) + r = source_windows(self.root, [self.rows[0]], 2) + j = score_windows(self.root, self.case('🧭', 1, 1), r)[0] + self.assertFalse(j['sufficientSourceEvidence']) + self.assertEqual(j['missingAfterIndentNormalization'][0]['returned'], '�') + self.assertEqual(r['sourceBytes'], 2) + + def test_source_drift_and_witness_drift_fail(self): + r = source_windows(self.root, self.rows, 100) + with self.assertRaises(ValueError): + score_windows(self.root, self.case('wrong'), r) + (self.root / 'x').write_bytes(b'changed') + with self.assertRaises(ValueError): + score_windows(self.root, self.case(), r) + + def test_interval_hash_counts_and_line_tampering_fail(self): + original = source_windows(self.root, self.rows, 100) + for change in [dict(endByte=999), dict(startByte=True), dict(sourceSha256='0'), + dict(sourceBytes=0), dict(startLine=2)]: + r = copy.deepcopy(original) + r['windows'][0].update(change) + with self.subTest(change=change), self.assertRaises(ValueError): + score_windows(self.root, self.case(), r) + + def test_header_allowance_needs_identity_and_all_other_lines(self): + self.data = b'\n' * 454 + b'class _AtomicFile:\n def name(self): pass\n' + (self.root / 'x').write_bytes(self.data) + case = self.case('class _AtomicFile:\n def name(self): pass', 455, 456) + case['facts'][0]['id'] = 'click-1' + r = source_windows(self.root, [dict(file='x', line=456)], 100) + self.assertFalse(score_windows(self.root, case, r)[0]['sufficientSourceEvidence']) + self.assertTrue(score_windows(self.root, case, r, identity_verified=True)[0]['sufficientSourceEvidence']) + r = source_windows(self.root, [dict(file='x', line=456)], 1) + self.assertFalse(score_windows(self.root, case, r, identity_verified=True)[0]['sufficientSourceEvidence']) + + def test_file_and_row_bounds(self): + with self.assertRaises(ValueError): + source_windows(self.root, [self.rows[0]] * 10001, 10) + (self.root / 'x').write_bytes(b'a' * 4194305) + with self.assertRaises(ValueError): + source_windows(self.root, self.rows, 10) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index a70adf91f..1f94045c8 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -3139,6 +3139,85 @@ checks were not rerun. This strengthens semantic precision evidence for one Java source configuration; authored explanations, longer walks, functional communities, actual god-object judgments and held-out confirmation remain open. +## Explanation retrieval: paired source-budget sensitivity + +Registration `35cb4aee` freezes five per-subject source quotas before capture: +2,000, 4,000, 8,000, 16,000 and 32,000 bytes. All 20 existing responsibility +facts, five pinned repositories, exact identity constraints and source-window +rules remain fixed. Both tools receive one public resolver request and one +public neighbor request per subject. Only returned outgoing `contains`/`method` +anchors determine source selection. Sorted windows end at the next returned +anchor; the last window is limited to 4,096 bytes. No witness, source-content +ranking or extra query selects the source. The latest `f2cacfeb` binary and +`java-state-access-02` graphs are used; Graphify's frozen native graphs and +installed package hashes are unchanged. + +This is a known development panel. Earlier questions and failures informed the +choice to examine budget sensitivity. The results are **source-evidence +coverage**, with no authored answers, held-out claim or production change. + +| Source quota per subject | Compass facts | Graphify facts | Compass actual source bytes, five subjects | Graphify actual source bytes, five subjects | +| --- | ---: | ---: | ---: | ---: | +| 2,000 | 8/20 | 8/20 | 10,000 | 10,000 | +| 4,000 | 8/20 | 8/20 | 19,673 | 19,673 | +| 8,000 | 14/20 | 15/20 | 35,673 | 35,673 | +| 16,000 | 19/20 | 18/20 | 59,542 | 53,417 | +| 32,000 | 20/20 | 18/20 | 64,644 | 53,747 | + +Literal-witness counts are one lower for each tool at every quota. The prior +Click allowance remains explicit: the class header alone is absent, while the +source-validated owner and every other required line are present. It cannot be +applied when identity is unverified or another required line is missing. + +At 8,000 bytes, every membership row and retained source interval is unchanged +from the earlier 14/20 versus 15/20 control. Added Java/Rust field contacts do not +help this membership-only policy. Changed jsoup/WalkDir resolver responses carry +new graph/view digests; jsoup neighbor differences are community metadata and +the corresponding serialized-byte requirement. +Full payload differences are retained, not silently discarded. + +The remaining Compass fact at 16,000 bytes is WalkDir's symlink-loop handling. +Earlier fields, methods and intervening comments consume the quota before its +implementation is reached. At 32,000 bytes, all four WalkDir witnesses fit. +Graphify's two remaining facts are Redux enhancer delegation and the complete +observable/store API. Its earliest returned member begins after the enhancer +implementation, and its last-member window reaches only part of the observable +implementation before the fixed 4,096-byte cap. Raising the global quota cannot +repair those intervals. This does not establish that Graphify cannot retrieve +that code with a different workflow. + +Payload costs remain unequal. Across the five subjects, Compass resolver +responses contain 3,039 text / 21,983 wire bytes and neighbor responses contain +33,061 / 372,871 bytes. Graphify returns 619 / 1,094 resolver bytes and +6,980 / 7,534 neighbor bytes. Larger-quota Compass coverage also consumes more +actual source bytes. Equal retained-source ceilings therefore do not imply +equal context, compute or total information cost. + +The reusable `source_windows.py` evaluator bounds files, aggregate source reads, +anchors and quotas; validates every anchor, including omitted ones; preserves +raw byte cuts through UTF-8; and checks source hashes before scoring. Eleven +new tests cover budget exhaustion, deduplication, order, final-window caps, +invalid anchors, path/symlink escapes, corruption, Unicode and the constrained +Click allowance. All **187 benchmark tests and the product boundary pass**. +The separate artifact verifier checks all 20 public calls, 153 membership +anchors and 50 window/scoring arms from raw transcripts and pinned source. +Independent repeated verification is byte-identical. The unchanged historical +8,000-byte policy is also replayed directly against each newly captured response. + +Committed `explanation_budget_registration.json` and +`explanation_budget_review.json` retain the protocol, every budget result, +actual costs, misses and artifact hashes. External `explanation-budget-01` +contains capture/replay scripts, raw calls, literal source windows, payload +deltas and verification logs. No native Rust, JavaScript, platform or packaging +checks were rerun because the product is unchanged; version remains 0.3.30. + +A 20/20 witness score does not establish complete answers to the original +natural-language questions or behavior of unseen callees. The next production +work should improve bounded explanation evidence selection and context costs, +then evaluate authored answers under a separately registered common workflow. +God-object defect judgments, longer-walk quality, functional community quality +and held-out confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 1c80747a05a673f73aaa1af00503362785242df1 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:26:27 -0700 Subject: [PATCH 92/97] audit: register bounded explanation member focus experiment --- .../explanation_focus_registration.json | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 benchmarks/agent_query/explanation_focus_registration.json diff --git a/benchmarks/agent_query/explanation_focus_registration.json b/benchmarks/agent_query/explanation_focus_registration.json new file mode 100644 index 000000000..703eae314 --- /dev/null +++ b/benchmarks/agent_query/explanation_focus_registration.json @@ -0,0 +1,53 @@ +{ + "schema": "compass.explanation-focus-registration/1", + "baselineCommit": "e445c5c6891432c30771a316c48d7ebda331c3ef", + "scope": "Known development questions; additive opt-in member-name focus, not a held-out experiment or a new paired superiority score.", + "sourceQuestions": "benchmarks/agent_query/responsibility_questions_panel_a.json", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "tasks": [ + { + "repository": "chi", + "symbol": "Mux", + "file": "mux.go", + "startLine": 21, + "kind": "struct" + }, + { + "repository": "click", + "symbol": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 455, + "kind": "class" + }, + { + "repository": "jsoup", + "symbol": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43, + "kind": "class" + }, + { + "repository": "redux", + "symbol": "createStore", + "file": "src/createStore.ts", + "startLine": 86, + "kind": "function" + }, + { + "repository": "walkdir", + "symbol": "IntoIter", + "file": "src/lib.rs", + "startLine": 566, + "kind": "struct" + } + ], + "graphRun": "java-state-access-02/run.json", + "graphRunSha256": "c315bffe56cbb7a7684c088e0219fc85ba9ef0681a39463608845691522d21ff", + "baselineBinary": "java-state-access-02/compass", + "baselineBinarySha256": "5f6ff5f9b7b13866fdc7435e7a05888d3a95a0b23636be89cf45bc3478660139", + "policy": "Optional --member-focus TEXT with --source-members. Use existing query_terms normalization for TEXT; distinct searchable terms, maximum 32, input maximum 4096 bytes. Score each discovered callable by count of distinct focus terms occurring in its recorded name after existing identifier tokenization and canonical code-token normalization. Rank by descending count then existing source file/byte range/exact ID order. Do not read source to rank, use oracle witnesses, follow calls, collapse overloads, or select between ambiguous roots. Zero matches preserve original source order. Preserve existing discovery, metadata, verification and shared source-byte limits.", + "capture": "Use source-validated exact root IDs from explanation-budget-01 public resolver capture. Run each latest Compass subject with existing --source-members and with --member-focus set to its full unchanged natural-language question. Use --budget 2000 --max-source-bytes 8000 for every invocation. Replay old unfocused behavior against frozen baseline binary. Preserve all 20 facts, source bytes, complete stdout/stderr, hashes, source verification statuses, omitted and unavailable counts. No per-subject focus tuning after results.", + "scoring": "Literal source-witness coverage is primary. Native callable spans omit decorators and may be insufficient. Report all losses and remaining misses; no class-header allowance without the full rest of the witness. Do not combine native member scores with the symmetric public-neighbor source-window control.", + "fairness": "Graphify has no equivalent native member-source/focus flag in the recorded interface. This is a Compass before/after experiment only. Retain the paired 8k control at 14/20 versus 15/20; evaluate any future common focus policy separately on both tools before claiming a paired gain.", + "verification": "Native tests cover later matching members under a small shared budget, zero-match/default equivalence, duplicate terms, camel/snake case, Unicode, deterministic shuffled graph order, ambiguous and stale sources, source read bounds and CLI argument/output contracts. Run native baseline and relevant CLI gates; hash the final binary and unchanged production sources before real captures." +} From 293582c38085a195b9a8272c8828196bc3724aaf Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:37:42 -0700 Subject: [PATCH 93/97] feat(query): prioritize focused member names in explanation source --- CHANGELOG.md | 5 + COMPATIBILITY.md | 10 + crates/compass-cli/src/help.rs | 2 +- crates/compass-cli/src/lib.rs | 57 +++++- crates/compass-cli/tests/code_query_cli.rs | 97 +++++++++ .../compass-query/src/explanation_members.rs | 80 +++++++- crates/compass-query/src/lib.rs | 5 +- .../tests/explanation_members.rs | 191 +++++++++++++++++- docs/reference/outputs.md | 9 + 9 files changed, 441 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8b33932..051e58dfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Add opt-in `explain --source-members --member-focus TEXT` to prioritize + normalized member-name matches within the existing shared source budget. + Preserve source order for ties, retain unmatched candidates and report the + lexical matches without claiming behavioral relevance. + - Emit Java field-access references from bounded lexical scope and receiver evidence, preserving shadowing, field identity and parallel source occurrences. Invalidate prior AST caches; retain unsupported targets without name fallback. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index e300e9673..02efd34dd 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -522,6 +522,16 @@ exhaustion reports truncation and omitted member counts. Individual source failures remain visible without suppressing other valid excerpts. Stored source digests and containment checks use the existing source reader. +Optional `--member-focus TEXT` requires member mode. It ranks callables by the +number of distinct normalized focus terms in their recorded names, then by the +original source order. Existing query-term and identifier normalization apply; +input is limited to 4,096 bytes and 32 distinct searchable terms. Empty or +unsearchable focus is rejected. Duplicate terms do not add weight. No matches +preserve source order; unmatched members remain candidates. Source is not read +to rank, and focus does not resolve ambiguous roots or relax any existing limit. +The report names focus terms and each retained member's lexical matches. These +matches do not establish responsibility, behavior or semantic relevance. + This is an additive CLI option and query API, with no graph or shared-output schema change. The text reports exact member IDs, source anchors, verification status, retained source bytes, and unavailable/omitted counts. It is structural diff --git a/crates/compass-cli/src/help.rs b/crates/compass-cli/src/help.rs index f8d446b02..a3f1d7817 100644 --- a/crates/compass-cli/src/help.rs +++ b/crates/compass-cli/src/help.rs @@ -431,7 +431,7 @@ const PAGES: &[Page] = &[ "explain", "Explain a node and its important relationships", ["compass explain [OPTIONS]"], - "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include declaration source with verification status [default]\n --source-members Show recorded callable members instead of the declaration\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound, shared by members [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node. A stored symbol digest must match; missing digests produce an explicitly unverified excerpt. A mismatch or malformed digest reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing.\n --source-members follows directed recorded containment through nested types, in source order.\n It shares one source-byte budget (maximum 1 MiB); omissions and unavailable members remain explicit." + "Arguments:\n Node ID, name, label, or qualified name\n\nOptions:\n --budget Approximate tokens per page [default: 2000]\n --page Connection or ambiguity page, starting at 1 [default: 1]\n --source Include declaration source with verification status [default]\n --source-members Show recorded callable members instead of the declaration\n --member-focus Prioritize matching member names; requires --source-members\n --no-source Omit declaration source\n --root Repository root used to read source [default: current directory]\n --max-source-bytes Source-byte bound, shared by members [default: 4096]\n --format Shared output contract [default: text]\n --graph Read a graph JSON file\n --at Use an immutable Git revision; conflicts with --graph\n\nExamples:\n compass explain PaymentService\n compass explain PaymentService --no-source\n compass explain PaymentService --budget 8000\n compass explain PaymentService --page 2\n compass explain auth --at HEAD~5 --format agent-json\n\nNotes:\n Source is read only for one source-backed node. A stored symbol digest must match; missing digests produce an explicitly unverified excerpt. A mismatch or malformed digest reports SOURCE unavailable; ambiguous or unsourced targets keep the candidate list instead of guessing.\n --source-members follows directed recorded containment through nested types, in source order.\n --member-focus ranks distinct normalized name matches first, then source order; it does not filter members.\n It shares one source-byte budget (maximum 1 MiB); omissions and unavailable members remain explicit." ), page!( "architecture", diff --git a/crates/compass-cli/src/lib.rs b/crates/compass-cli/src/lib.rs index 3ffb4dcdd..fb74cbabc 100644 --- a/crates/compass-cli/src/lib.rs +++ b/crates/compass-cli/src/lib.rs @@ -85,11 +85,11 @@ use compass_output::{ use compass_prs::{ProcessRunner, SystemRunner}; use compass_query::{ DEFAULT_AFFECTED_RELATIONS, DEFAULT_DISCOVERY_TEXT_TOKEN_BUDGET, DEFAULT_PATH_DEPTH_LIMIT, - DEFAULT_TEXT_TOKEN_BUDGET, DiscoveryTextPageOptions, TextPageOptions, TraversalMode, - discovery_request_digest, explanation_member_sources, explanation_source, format_affected, - format_benchmark, open as open_code_query, open_with_verified_document, query_graph_text_page, - render_discovery_text_page_with_prefix, render_explanation_page, - render_shortest_path_with_limit, run_benchmark, + DEFAULT_TEXT_TOKEN_BUDGET, DiscoveryTextPageOptions, ExplanationMemberFocus, TextPageOptions, + TraversalMode, discovery_request_digest, explanation_member_sources_with_focus, + explanation_source, format_affected, format_benchmark, open as open_code_query, + open_with_verified_document, query_graph_text_page, render_discovery_text_page_with_prefix, + render_explanation_page, render_shortest_path_with_limit, run_benchmark, }; use compass_semantic::{ CachedCorpusExtractionOptions, CorpusExtractionOptions, PreparedDocumentInputs, @@ -6652,6 +6652,7 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { let mut page = 1_usize; let mut with_source = true; let mut with_member_source = false; + let mut member_focus = None; let mut source_root = std::path::PathBuf::from("."); let mut max_source_bytes = DEFAULT_EXPLAIN_SOURCE_BYTES; let mut index = 1; @@ -6661,6 +6662,26 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { with_member_source = true; index += 1; } + value if value == "--member-focus" || value.starts_with("--member-focus=") => { + if member_focus.is_some() { + return Outcome::failure( + "error: --member-focus may be supplied only once".to_owned(), + ); + } + let (text, consumed) = if value == "--member-focus" { + let Some(text) = args.get(index + 1) else { + return Outcome::failure("error: --member-focus requires text".to_owned()); + }; + (text.as_str(), 2) + } else { + (&value[15..], 1) + }; + member_focus = match ExplanationMemberFocus::new(text) { + Ok(focus) => Some(focus), + Err(error) => return Outcome::failure(format!("error: {error}")), + }; + index += consumed; + } "--source" => { with_source = true; index += 1; @@ -6757,6 +6778,9 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { if with_member_source && !with_source { return Outcome::failure("error: --source-members conflicts with --no-source".to_owned()); } + if member_focus.is_some() && !with_member_source { + return Outcome::failure("error: --member-focus requires --source-members".to_owned()); + } if with_member_source && (max_source_bytes > 1_048_576 || label.len() > 4096) { return Outcome::failure( "error: member source accepts at most 1048576 source bytes and a 4096-byte selector" @@ -6795,6 +6819,7 @@ fn command_explain(frontend: Frontend, args: &[String]) -> Outcome { label, &source_root, max_source_bytes, + member_focus.as_ref(), ) } else if with_source { append_explanation_source(output, &loaded.graph, label, &source_root, max_source_bytes) @@ -6868,21 +6893,39 @@ fn append_explanation_member_sources( label: &str, root: &std::path::Path, max_source_bytes: u64, + focus: Option<&ExplanationMemberFocus>, ) -> String { - match explanation_member_sources(graph, label, root, max_source_bytes) { + match explanation_member_sources_with_focus(graph, label, root, max_source_bytes, focus) { Ok(report) => { let unavailable = report.members.iter().filter(|m| m.source.is_err()).count(); + let order = if focus.is_some() { + "member-name focus, then source order" + } else { + "source order" + }; output.push_str(&format!( - "\n\nMEMBER SOURCES owner={} retained={} omitted={} unavailable={} source_bytes={} truncated={}\nRecorded containment; source order; shared {}-byte source budget.", + "\n\nMEMBER SOURCES owner={} retained={} omitted={} unavailable={} source_bytes={} truncated={}\nRecorded containment; {order}; shared {}-byte source budget.", serde_json::json!(report.root.id), report.members.len(), report.omitted_members, unavailable, report.source_bytes, report.truncated, max_source_bytes, )); + if focus.is_some() { + output.push_str(&format!( + "\nFocus terms: {} (lexical name matches; not behavioral evidence).", + serde_json::json!(report.focus_terms) + )); + } for member in report.members { output.push_str(&format!( "\n\nMEMBER {} {}", serde_json::json!(member.node.id), serde_json::json!(member.node.label()) )); + if focus.is_some() { + output.push_str(&format!( + "\nMatched focus terms: {}", + serde_json::json!(member.matched_focus_terms) + )); + } match member.source { Ok(excerpt) => { let retained_bytes = excerpt.source.len() as u64; diff --git a/crates/compass-cli/tests/code_query_cli.rs b/crates/compass-cli/tests/code_query_cli.rs index 157ed5898..40ece4cf3 100644 --- a/crates/compass-cli/tests/code_query_cli.rs +++ b/crates/compass-cli/tests/code_query_cli.rs @@ -2376,6 +2376,103 @@ fn explain_member_source_reaches_implementations_outside_type_declarations() Ok(()) } +#[test] +fn explain_member_focus_prioritizes_names_and_validates_its_options() -> Result<(), Box> +{ + use sha2::{Digest, Sha256}; + let directory = tempfile::tempdir()?; + let parts = [ + "struct Owner {}\n", + "fn early() { first(); }\n", + "fn check_loop() { later(); }\n", + ]; + std::fs::write(directory.path().join("lib.rs"), parts.concat())?; + let mut offset = 0; + let nodes = [("owner", "Owner", "struct"), ("early", "early", "method"), ("loop", "check_loop", "method")] + .into_iter().enumerate().map(|(index, (id, name, kind))| { + let text = parts[index]; + let start = offset; + offset += text.len(); + serde_json::json!({"id": id, "name": name, "kind": kind, + "source": {"file": "lib.rs", "startByte": start, "endByte": offset, "startLine": index+1, "endLine": index+1, "startColumn": 0, "endColumn": text.len()-1}, + "details": {"type": "symbol", "data": {"sourceDigest": format!("{:x}", Sha256::digest(text.as_bytes()))}} + }) + }).collect::>(); + let graph = directory.path().join("graph.json"); + std::fs::write( + &graph, + serde_json::json!({"directed":true,"multigraph":true,"nodes":nodes,"links":[ + {"source":"owner","target":"early","relation":"contains","confidence":"EXTRACTED"}, + {"source":"owner","target":"loop","relation":"contains","confidence":"EXTRACTED"} + ]}) + .to_string(), + )?; + let invoke = |extra: &[&str]| { + let mut args = vec![ + OsString::from("explain"), + OsString::from("owner"), + OsString::from("--root"), + directory.path().as_os_str().to_owned(), + OsString::from("--graph"), + graph.as_os_str().to_owned(), + ]; + args.extend(extra.iter().map(OsString::from)); + run(Frontend::Compass, args) + }; + let budget = parts[2].len().to_string(); + let baseline = invoke(&["--source-members", "--max-source-bytes", &budget]); + assert_eq!(baseline.code, 0, "{}", baseline.stderr); + assert!(baseline.stdout.contains("first();")); + assert!(!baseline.stdout.contains("later();")); + for format in ["text", "json", "agent-json"] { + let focused = invoke(&[ + "--source-members", + "--member-focus", + "loops", + "--max-source-bytes", + &budget, + "--format", + format, + ]); + assert_eq!(focused.code, 0, "{}", focused.stderr); + assert!(focused.stdout.contains("later();")); + assert!(!focused.stdout.contains("first();")); + assert!( + focused + .stdout + .contains("member-name focus, then source order") + ); + assert!(focused.stdout.contains("Matched focus terms:")); + assert!( + focused + .stdout + .contains("retained=1 omitted=1 unavailable=0") + ); + } + let equals = invoke(&["--source-members", "--member-focus=loops"]); + let spaced = invoke(&["--source-members", "--member-focus", "loops"]); + assert_eq!(equals.stdout, spaced.stdout); + for args in [ + vec!["--member-focus", "loops"], + vec!["--source-members", "--member-focus"], + vec!["--source-members", "--member-focus="], + vec!["--source-members", "--member-focus", "!!!"], + vec!["--source-members", "--no-source", "--member-focus", "loops"], + vec![ + "--source-members", + "--member-focus=loops", + "--member-focus=early", + ], + ] { + assert_ne!(invoke(&args).code, 0, "{args:?}"); + } + assert_ne!( + invoke(&["--source-members", "--member-focus", &"x".repeat(4097)]).code, + 0 + ); + Ok(()) +} + #[test] fn exact_search_cli_filters_explicitly_and_preserves_bounded_ambiguity() -> Result<(), Box> { diff --git a/crates/compass-query/src/explanation_members.rs b/crates/compass-query/src/explanation_members.rs index 3dac74ccc..783c08b52 100644 --- a/crates/compass-query/src/explanation_members.rs +++ b/crates/compass-query/src/explanation_members.rs @@ -4,7 +4,7 @@ use std::collections::{BTreeSet, VecDeque}; use std::path::Path; use compass_model::code_graph::NodeKind; -use compass_model::{EdgeRecord, Graph, NodeRecord}; +use compass_model::{EdgeRecord, Graph, NodeRecord, canonical_code_token, identifier_tokens}; use serde_json::Value; use crate::neighbors::bounded_json_size; @@ -25,11 +25,14 @@ const MAX_VERIFIED_SPAN_BYTES: u64 = 16_777_216; pub struct ExplainedMember<'a> { pub node: &'a NodeRecord, pub source: Result, + /// Lexical matches in the recorded name, not evidence of behavior. + pub matched_focus_terms: Vec, } #[derive(Debug)] pub struct ExplainedMembers<'a> { pub root: &'a NodeRecord, + pub focus_terms: Vec, /// Full recorded membership evidence, retaining parallel records. pub membership: Vec<&'a EdgeRecord>, pub members: Vec>, @@ -44,6 +47,44 @@ fn error(message: &str) -> ExplanationSourceError { ExplanationSourceError::Read(message.to_owned()) } +/// Validated lexical focus for recorded member names. No source is read to rank. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ExplanationMemberFocus { + terms: BTreeSet, +} + +impl ExplanationMemberFocus { + pub fn new(text: &str) -> Result { + if text.len() > 4096 { + return Err(error("member focus exceeds its 4096-byte limit")); + } + let terms = crate::query_terms(text) + .into_iter() + .collect::>(); + if terms.is_empty() || terms.len() > 32 { + return Err(error( + "member focus requires from 1 to 32 distinct searchable terms", + )); + } + Ok(Self { terms }) + } + + fn matches(&self, node: &NodeRecord) -> Vec { + let mut tokens = BTreeSet::new(); + // Match the lexical index's treatment of whole snake-case identifiers + // and their components; identifier_tokens deliberately retains '_'. + for token in identifier_tokens(&node.string("name")) { + tokens.insert(canonical_code_token(token.clone())); + if token.contains('_') { + for part in token.split('_').filter(|part| !part.is_empty()) { + tokens.insert(canonical_code_token(part.to_owned())); + } + } + } + self.terms.intersection(&tokens).cloned().collect() + } +} + fn kind(node: &NodeRecord) -> Option { if node.kind_name().len() > 64 { return None; @@ -63,6 +104,20 @@ pub fn explanation_member_sources<'a>( label: &str, root: &Path, max_source_bytes: u64, +) -> Result, ExplanationSourceError> { + explanation_member_sources_with_focus(graph, label, root, max_source_bytes, None) +} + +/// Prioritize distinct normalized name matches, then the original source order. +/// +/// Focus changes only presentation priority; it neither filters membership nor +/// resolves ambiguity. The unfocused API and zero-match ordering are preserved. +pub fn explanation_member_sources_with_focus<'a>( + graph: &'a Graph, + label: &str, + root: &Path, + max_source_bytes: u64, + focus: Option<&ExplanationMemberFocus>, ) -> Result, ExplanationSourceError> { if label.len() > 4096 || !(1..=MAX_SOURCE_BYTES).contains(&max_source_bytes) { return Err(error( @@ -152,13 +207,22 @@ pub fn explanation_member_sources<'a>( let key = anchor .as_ref() .map(|a| (a.file.clone(), a.start_byte, a.end_byte)); - (key, node) + let matches = focus.map(|f| f.matches(node)).unwrap_or_default(); + (key, node, matches) }) .collect::>(); - ordered.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.id.cmp(&b.1.id))); + ordered.sort_by(|a, b| { + b.2.len() + .cmp(&a.2.len()) + .then_with(|| a.0.cmp(&b.0)) + .then_with(|| a.1.id.cmp(&b.1.id)) + }); let total = ordered.len(); let mut report = ExplainedMembers { root: graph.node(seed), + focus_terms: focus + .map(|f| f.terms.iter().cloned().collect()) + .unwrap_or_default(), membership: membership.into_iter().map(|(_, edge)| edge).collect(), members: Vec::new(), omitted_members: 0, @@ -166,7 +230,7 @@ pub fn explanation_member_sources<'a>( verification_bytes_charged: 0, truncated: false, }; - for (_, node) in ordered { + for (_, node, matched_focus_terms) in ordered { let remaining = max_source_bytes.saturating_sub(report.source_bytes); if remaining == 0 { report.truncated = true; @@ -175,6 +239,7 @@ pub fn explanation_member_sources<'a>( let Some(anchor) = node_source_anchor(node) else { report.members.push(ExplainedMember { node, + matched_focus_terms, source: Err(ExplanationSourceError::Unsourced { label: node.id.clone(), }), @@ -184,6 +249,7 @@ pub fn explanation_member_sources<'a>( let Some(span_bytes) = anchor.end_byte.checked_sub(anchor.start_byte) else { report.members.push(ExplainedMember { node, + matched_focus_terms, source: Err(error("recorded member source span is inverted")), }); continue; @@ -210,7 +276,11 @@ pub fn explanation_member_sources<'a>( report.source_bytes += excerpt.source.len() as u64; report.truncated |= excerpt.truncated; } - report.members.push(ExplainedMember { node, source }); + report.members.push(ExplainedMember { + node, + source, + matched_focus_terms, + }); } report.omitted_members = total.saturating_sub(report.members.len()); Ok(report) diff --git a/crates/compass-query/src/lib.rs b/crates/compass-query/src/lib.rs index 0aed34a76..84d0e501e 100644 --- a/crates/compass-query/src/lib.rs +++ b/crates/compass-query/src/lib.rs @@ -41,7 +41,10 @@ pub use discovery_text::{ discovery_response_digest, discovery_result_envelope, render_discovery_text_page, render_discovery_text_page_with_prefix, }; -pub use explanation_members::{ExplainedMember, ExplainedMembers, explanation_member_sources}; +pub use explanation_members::{ + ExplainedMember, ExplainedMembers, ExplanationMemberFocus, explanation_member_sources, + explanation_member_sources_with_focus, +}; pub use graph_engine::{ DirectGraphEngine, EffectiveGraphEngine, GraphEngine, JsonGraphEngine, StoreGraphEngine, open_graph_engine, diff --git a/crates/compass-query/tests/explanation_members.rs b/crates/compass-query/tests/explanation_members.rs index aa41c689c..009129583 100644 --- a/crates/compass-query/tests/explanation_members.rs +++ b/crates/compass-query/tests/explanation_members.rs @@ -2,7 +2,9 @@ use std::error::Error; use std::fs; use compass_model::{Graph, GraphDocument}; -use compass_query::explanation_member_sources; +use compass_query::{ + ExplanationMemberFocus, explanation_member_sources, explanation_member_sources_with_focus, +}; use serde_json::{Value, json}; use sha2::{Digest, Sha256}; @@ -44,6 +46,193 @@ fn graph(value: Value) -> Result> { >(value)?)?) } +#[test] +fn focus_reaches_later_members_with_the_same_source_budget() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let focus = ExplanationMemberFocus::new("later later")?; + for reversed in [false, true] { + let mut doc = document()?; + if reversed { + doc["nodes"].as_array_mut().ok_or("nodes")?.reverse(); + doc["links"].as_array_mut().ok_or("links")?.reverse(); + } + let graph = graph(doc)?; + let baseline = + explanation_member_sources(&graph, "owner", root.path(), LATER.len() as u64)?; + assert_eq!(baseline.members[0].node.id, "first"); + let report = explanation_member_sources_with_focus( + &graph, + "owner", + root.path(), + LATER.len() as u64, + Some(&focus), + )?; + assert_eq!(report.focus_terms, ["later"]); + assert_eq!(report.members.len(), 1); + assert_eq!(report.members[0].node.id, "later"); + assert_eq!(report.members[0].matched_focus_terms, ["later"]); + assert_eq!( + report.members[0] + .source + .as_ref() + .map_err(|e| e.to_string())? + .source, + LATER + ); + assert_eq!(report.source_bytes, LATER.len() as u64); + assert_eq!(report.verification_bytes_charged, LATER.len() as u64); + assert_eq!(report.omitted_members, 1); + assert!(report.truncated); + assert_eq!(report.membership.len(), baseline.membership.len()); + } + Ok(()) +} + +#[test] +fn absent_focus_matches_preserve_order_and_do_not_filter_members() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + let graph = graph(document()?)?; + let focus = ExplanationMemberFocus::new("nothing_matches")?; + let baseline = explanation_member_sources(&graph, "owner", root.path(), 8000)?; + for focus in [None, Some(&focus)] { + let report = + explanation_member_sources_with_focus(&graph, "owner", root.path(), 8000, focus)?; + assert_eq!( + report + .members + .iter() + .map(|m| m.node.id.as_str()) + .collect::>(), + ["first", "later"] + ); + assert!( + report + .members + .iter() + .all(|m| m.matched_focus_terms.is_empty()) + ); + assert_eq!(report.source_bytes, baseline.source_bytes); + assert_eq!( + report.verification_bytes_charged, + baseline.verification_bytes_charged + ); + assert_eq!(report.truncated, baseline.truncated); + } + Ok(()) +} + +#[test] +fn focus_counts_distinct_normalized_name_terms_and_keeps_ties_stable() -> Result<(), Box> +{ + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE)?; + for (first_name, later_name, question, expected) in [ + ( + "check", + "checkLoop", + "checking loops", + vec!["later", "first"], + ), + ( + "check_loop", + "checkLoop", + "checking loops", + vec!["first", "later"], + ), + ("other", "Résumé", "résumé", vec!["later", "first"]), + ] { + let mut doc = document()?; + doc["nodes"][1]["name"] = json!(first_name); + doc["nodes"][3]["name"] = json!(later_name); + let graph = graph(doc)?; + let focus = ExplanationMemberFocus::new(question)?; + let report = explanation_member_sources_with_focus( + &graph, + "owner", + root.path(), + 8000, + Some(&focus), + )?; + assert_eq!( + report + .members + .iter() + .map(|m| m.node.id.as_str()) + .collect::>(), + expected + ); + } + Ok(()) +} + +#[test] +fn focus_does_not_read_source_to_rank_or_suppress_stale_failures() -> Result<(), Box> { + let root = tempfile::tempdir()?; + fs::write(root.path().join("lib.rs"), SOURCE.replace("two", "bad"))?; + let graph = graph(document()?)?; + // A body-only token must not rank a member. The matching name is stale, + // remains first, and its failed verification still consumes work. + let body_focus = ExplanationMemberFocus::new("two")?; + let report = explanation_member_sources_with_focus( + &graph, + "owner", + root.path(), + 8000, + Some(&body_focus), + )?; + assert_eq!(report.members[0].node.id, "first"); + let focus = ExplanationMemberFocus::new("later")?; + let report = + explanation_member_sources_with_focus(&graph, "owner", root.path(), 8000, Some(&focus))?; + assert_eq!(report.members[0].node.id, "later"); + assert!(report.members[0].source.is_err()); + assert!(report.members[1].source.is_ok()); + assert_eq!(report.source_bytes, FIRST.len() as u64); + assert_eq!( + report.verification_bytes_charged, + (FIRST.len() + LATER.len()) as u64 + ); + Ok(()) +} + +#[test] +fn focus_does_not_resolve_ambiguous_roots() -> Result<(), Box> { + let root = tempfile::tempdir()?; + let mut doc = document()?; + doc["nodes"].as_array_mut().ok_or("nodes")?.push(node( + "other", + "Owner", + "struct", + "struct Owner {}", + )?); + let graph = graph(doc)?; + let focus = ExplanationMemberFocus::new("later")?; + assert!( + explanation_member_sources_with_focus(&graph, "Owner", root.path(), 8000, Some(&focus)) + .is_err() + ); + Ok(()) +} + +#[test] +fn focus_limits_reject_invalid_inputs_without_source_access() -> Result<(), Box> { + for text in [ + String::new(), + "!!!".to_owned(), + "x".repeat(4097), + (0..33) + .map(|i| format!("term{i}")) + .collect::>() + .join(" "), + ] { + assert!(ExplanationMemberFocus::new(&text).is_err()); + } + assert!(ExplanationMemberFocus::new(&"later ".repeat(100)).is_ok()); + Ok(()) +} + #[test] fn members_outside_the_owner_span_keep_nested_direction_and_parallel_evidence() -> Result<(), Box> { diff --git a/docs/reference/outputs.md b/docs/reference/outputs.md index e3e0ab1d8..cb88ad414 100644 --- a/docs/reference/outputs.md +++ b/docs/reference/outputs.md @@ -1380,6 +1380,15 @@ declaration span. The optional mode replaces the declaration excerpt. It follows outgoing containment through nested types and orders members by source location; it does not choose members based on an inferred responsibility. +To prioritize a topic within that recorded membership, add +`--member-focus "symlink loops"`. Members whose names contain more distinct +normalized query terms appear first; source order breaks ties. Unmatched members +remain eligible and source reads still share one byte budget. This is a lexical +name hint, not a synthesized answer. The text reports the normalized focus and +matches per retained member. Focus requires `--source-members`, accepts at most +4,096 bytes / 32 distinct searchable terms, and never disambiguates an owner. +Without focus, the existing source order and output remain unchanged. + `MEMBER SOURCES` reports retained, omitted and unavailable members, total source bytes, and truncation. Each `MEMBER` has an exact ID followed by its source and verification status, or an explicit source error. The byte budget is shared From 9f17e0ccd79f373d3201ec57ad930719c8370be9 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:43:24 -0700 Subject: [PATCH 94/97] audit: qualify member focus on five repository questions --- benchmarks/agent_query/README.md | 24 + .../agent_query/explanation_focus_review.json | 2971 +++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 83 + 3 files changed, 3078 insertions(+) create mode 100644 benchmarks/agent_query/explanation_focus_review.json diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index 9346e35af..ba2abf536 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -683,3 +683,27 @@ External `explanation-budget-01` contains the collector, independent verifier, 8,000-byte source controls reproduce the old bytes and outcomes. The audit explains source-order starvation, Graphify's two Redux interval gaps, and the much larger Compass graph payloads. No production code changes in this arm. + +### Native member-name focus + +`explanation_focus_registration.json` fixes a Compass before/after experiment +using each full original question and the same 8,000-byte source quota. Product +commit `293582c3` adds optional `--source-members --member-focus TEXT`: rank +recorded callable names by distinct normalized term matches, then source order. +No source is read to rank; unmatched members and all existing limits remain. + +`explanation_focus_review.json` records a gain from 14/20 to 15/20 supported +facts, with no losses on this known development panel. Chi gains routing +implementation evidence. Literal coverage is 5/20 versus 6/20 before indentation +normalization. Actual retained source stays 28,129 bytes; stdout and charged +verification work increase. Six Redux excerpts remain explicitly unverified +because their graph nodes lack stored source digests. + +All 15 native invocations, ordering decisions, source intervals and provenance +statuses replay under external `member-focus-02`; round 01 retains the initial +compile failure. The corrected verifier retains its failed assumption about +Redux digests. Native and benchmark checks are listed in the main audit. + +This is not a paired Graphify result or authored-answer score. The separate +symmetric 8,000-byte neighbor-window control remains 14/20 versus 15/20. Remaining +WalkDir loop evidence shows why lexical name matching alone is insufficient. diff --git a/benchmarks/agent_query/explanation_focus_review.json b/benchmarks/agent_query/explanation_focus_review.json new file mode 100644 index 000000000..ffd663a75 --- /dev/null +++ b/benchmarks/agent_query/explanation_focus_review.json @@ -0,0 +1,2971 @@ +{ + "schema": "compass.explanation-focus-review/1", + "registration": "benchmarks/agent_query/explanation_focus_registration.json", + "registrationSha256": "8287faea7015f865cbea8b5088ef20ca04d10ee1f367724327ac02ce3fa80cc6", + "registrationCommit": "1c80747a", + "productCommit": "293582c38085a195b9a8272c8828196bc3724aaf", + "evaluatedBinarySha256": "e33934b94d1e254ba59906fddece098d8433bf7e31af746e46aac3dfc6744f97", + "scope": "Known development questions; additive opt-in member-name focus, not a held-out experiment or a new paired superiority score.", + "summary": { + "baseline": { + "facts": 20, + "literalWitnessFacts": 5, + "sourceEvidenceFacts": 14, + "sourceBytes": 28129, + "stdoutBytes": 58506, + "verificationBytesCharged": 28636, + "unverifiedSourceExcerpts": 6 + }, + "unfocused": { + "facts": 20, + "literalWitnessFacts": 5, + "sourceEvidenceFacts": 14, + "sourceBytes": 28129, + "stdoutBytes": 58506, + "verificationBytesCharged": 28636, + "unverifiedSourceExcerpts": 6 + }, + "focused": { + "facts": 20, + "literalWitnessFacts": 6, + "sourceEvidenceFacts": 15, + "sourceBytes": 28129, + "stdoutBytes": 60942, + "verificationBytesCharged": 29287, + "unverifiedSourceExcerpts": 6 + } + }, + "results": [ + { + "repository": "chi", + "arm": "baseline", + "focusTerms": [], + "sourceBytes": 8000, + "verificationBytesCharged": 8102, + "stdoutBytes": 18858, + "omittedMembers": 5, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 3, + "sourceEvidenceFacts": 3, + "members": [ + { + "id": "sha256:0b03f729c4b4c93626eb48b705704c97e8d051ebb76354fdb1028644c0c003f3", + "name": ".ServeHTTP()", + "matchedTerms": [], + "startLine": 63, + "endLine": 92, + "sourceBytes": 1039, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "name": ".Use()", + "matchedTerms": [], + "startLine": 100, + "endLine": 105, + "sourceBytes": 225, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b6f4933c875274b7724a57dc037f278cef17603b5a17c0123f74f09c4859ade3", + "name": ".Handle()", + "matchedTerms": [], + "startLine": 109, + "endLine": 117, + "sourceBytes": 268, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:00488c58ae8e3d2aea91ae763f174cddbcbfa1edb9fda7a9650119778dac1272", + "name": ".HandleFunc()", + "matchedTerms": [], + "startLine": 121, + "endLine": 123, + "sourceBytes": 104, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "name": ".Method()", + "matchedTerms": [], + "startLine": 127, + "endLine": 133, + "sourceBytes": 233, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "name": ".MethodFunc()", + "matchedTerms": [], + "startLine": 137, + "endLine": 139, + "sourceBytes": 120, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c68a79f08a92785f7b855336a541459e9fdc62201d4430b22c5a267720fe0e2e", + "name": ".Connect()", + "matchedTerms": [], + "startLine": 143, + "endLine": 145, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7eaf9ccc10f79e519444ce15ca5e733334b9706658f775085bcd446e28339098", + "name": ".Delete()", + "matchedTerms": [], + "startLine": 149, + "endLine": 151, + "sourceBytes": 109, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:cc338ab0fc91b15feee67ab6ef6b5cb5f32500ccb4011e5a672be9f91a09e728", + "name": ".Get()", + "matchedTerms": [], + "startLine": 155, + "endLine": 157, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f6e8a7560084fdf9a66af0e486fd6ceed76e6607c6ede28fdb1bf8c314ed4a19", + "name": ".Head()", + "matchedTerms": [], + "startLine": 161, + "endLine": 163, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:86384a3e1b4355bef88f940fa46a5af66a9ba09309b1555884da609ceabad4b5", + "name": ".Options()", + "matchedTerms": [], + "startLine": 167, + "endLine": 169, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:940225f9f7c63134e90084b0f543639b43293824ada2130d5aa1c37c6e3de01f", + "name": ".Patch()", + "matchedTerms": [], + "startLine": 173, + "endLine": 175, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:4dc5dca620fc82d5378b46e6646c03877017e141daa1474dab9ce69c1d4cfc56", + "name": ".Post()", + "matchedTerms": [], + "startLine": 179, + "endLine": 181, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:af7652f3b85f23544717c7555bffe88a5b3ee1fc645491c1dd5d6fd3e5367bbe", + "name": ".Put()", + "matchedTerms": [], + "startLine": 185, + "endLine": 187, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e2015cff9c7be8cb29614b5a597580ec714c51b3a9bb8622c8a0c15674b79ade", + "name": ".Query()", + "matchedTerms": [], + "startLine": 191, + "endLine": 193, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8fb9d62ca6fa95670412c09930734152e8175fa59cb9ba6e74c30eba422ed530", + "name": ".Trace()", + "matchedTerms": [], + "startLine": 197, + "endLine": 199, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:eb197ef3e44ebc05271156e9e59ec93db988e2df4ca08efea5788baa91401518", + "name": ".NotFound()", + "matchedTerms": [], + "startLine": 203, + "endLine": 219, + "sourceBytes": 419, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1ac18c4f1a692f25a73fdc98b240ed310b2c4a6cf5065ffde2524dd41b7a1f78", + "name": ".MethodNotAllowed()", + "matchedTerms": [], + "startLine": 223, + "endLine": 239, + "sourceBytes": 467, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "name": ".With()", + "matchedTerms": [], + "startLine": 242, + "endLine": 263, + "sourceBytes": 682, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0ac374940442cf9a4d0a0d3270546ca4b87d036fd9ec4aa8f8e48ca2d49e4407", + "name": ".Group()", + "matchedTerms": [], + "startLine": 268, + "endLine": 274, + "sourceBytes": 106, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:395cabe22892d311ed21d29bf7a01cb9d9d2eb1681c634bfd76121bdcf20b34f", + "name": ".Route()", + "matchedTerms": [], + "startLine": 278, + "endLine": 286, + "sourceBytes": 258, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:29cd93db4ec894393cf257f027b8988a07ee77181191167f3eb2037f2cfdf5be", + "name": ".Mount()", + "matchedTerms": [], + "startLine": 295, + "endLine": 354, + "sourceBytes": 1986, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0fa43499c8e3e06b415a5fd6585b87c3fd541d6c883379d774c9247edeb94951", + "name": ".Routes()", + "matchedTerms": [], + "startLine": 358, + "endLine": 360, + "sourceBytes": 60, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:2e266536f77bb3848784eb117cb5935bce2575bba587e1653ce1e6f9c056ebef", + "name": ".Middlewares()", + "matchedTerms": [], + "startLine": 363, + "endLine": 365, + "sourceBytes": 67, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f0a27beb5e2a6ff88e563c8dbed4dc6b6a697c1895287734f456edcb78d3ada3", + "name": ".Match()", + "matchedTerms": [], + "startLine": 373, + "endLine": 375, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b84d22d8b0ff377822bcdc4cb0d4aaf092970f4a85c5e1107b4d09b1592c015d", + "name": ".Find()", + "matchedTerms": [], + "startLine": 382, + "endLine": 408, + "sourceBytes": 537, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1580e9037451fb2ed515953396059772522a762628fd281119ff95ef6389357a", + "name": ".NotFoundHandler()", + "matchedTerms": [], + "startLine": 412, + "endLine": 417, + "sourceBytes": 138, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6d0c29b15fa70de4383c9685161ae0dea68e92fdbb120770c4a7315e8a2fc10b", + "name": ".MethodNotAllowedHandler()", + "matchedTerms": [], + "startLine": 421, + "endLine": 426, + "sourceBytes": 116, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493 + ] + } + ] + }, + { + "repository": "chi", + "arm": "unfocused", + "focusTerms": [], + "sourceBytes": 8000, + "verificationBytesCharged": 8102, + "stdoutBytes": 18858, + "omittedMembers": 5, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 3, + "sourceEvidenceFacts": 3, + "members": [ + { + "id": "sha256:0b03f729c4b4c93626eb48b705704c97e8d051ebb76354fdb1028644c0c003f3", + "name": ".ServeHTTP()", + "matchedTerms": [], + "startLine": 63, + "endLine": 92, + "sourceBytes": 1039, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "name": ".Use()", + "matchedTerms": [], + "startLine": 100, + "endLine": 105, + "sourceBytes": 225, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b6f4933c875274b7724a57dc037f278cef17603b5a17c0123f74f09c4859ade3", + "name": ".Handle()", + "matchedTerms": [], + "startLine": 109, + "endLine": 117, + "sourceBytes": 268, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:00488c58ae8e3d2aea91ae763f174cddbcbfa1edb9fda7a9650119778dac1272", + "name": ".HandleFunc()", + "matchedTerms": [], + "startLine": 121, + "endLine": 123, + "sourceBytes": 104, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "name": ".Method()", + "matchedTerms": [], + "startLine": 127, + "endLine": 133, + "sourceBytes": 233, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "name": ".MethodFunc()", + "matchedTerms": [], + "startLine": 137, + "endLine": 139, + "sourceBytes": 120, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c68a79f08a92785f7b855336a541459e9fdc62201d4430b22c5a267720fe0e2e", + "name": ".Connect()", + "matchedTerms": [], + "startLine": 143, + "endLine": 145, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7eaf9ccc10f79e519444ce15ca5e733334b9706658f775085bcd446e28339098", + "name": ".Delete()", + "matchedTerms": [], + "startLine": 149, + "endLine": 151, + "sourceBytes": 109, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:cc338ab0fc91b15feee67ab6ef6b5cb5f32500ccb4011e5a672be9f91a09e728", + "name": ".Get()", + "matchedTerms": [], + "startLine": 155, + "endLine": 157, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f6e8a7560084fdf9a66af0e486fd6ceed76e6607c6ede28fdb1bf8c314ed4a19", + "name": ".Head()", + "matchedTerms": [], + "startLine": 161, + "endLine": 163, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:86384a3e1b4355bef88f940fa46a5af66a9ba09309b1555884da609ceabad4b5", + "name": ".Options()", + "matchedTerms": [], + "startLine": 167, + "endLine": 169, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:940225f9f7c63134e90084b0f543639b43293824ada2130d5aa1c37c6e3de01f", + "name": ".Patch()", + "matchedTerms": [], + "startLine": 173, + "endLine": 175, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:4dc5dca620fc82d5378b46e6646c03877017e141daa1474dab9ce69c1d4cfc56", + "name": ".Post()", + "matchedTerms": [], + "startLine": 179, + "endLine": 181, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:af7652f3b85f23544717c7555bffe88a5b3ee1fc645491c1dd5d6fd3e5367bbe", + "name": ".Put()", + "matchedTerms": [], + "startLine": 185, + "endLine": 187, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e2015cff9c7be8cb29614b5a597580ec714c51b3a9bb8622c8a0c15674b79ade", + "name": ".Query()", + "matchedTerms": [], + "startLine": 191, + "endLine": 193, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8fb9d62ca6fa95670412c09930734152e8175fa59cb9ba6e74c30eba422ed530", + "name": ".Trace()", + "matchedTerms": [], + "startLine": 197, + "endLine": 199, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:eb197ef3e44ebc05271156e9e59ec93db988e2df4ca08efea5788baa91401518", + "name": ".NotFound()", + "matchedTerms": [], + "startLine": 203, + "endLine": 219, + "sourceBytes": 419, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1ac18c4f1a692f25a73fdc98b240ed310b2c4a6cf5065ffde2524dd41b7a1f78", + "name": ".MethodNotAllowed()", + "matchedTerms": [], + "startLine": 223, + "endLine": 239, + "sourceBytes": 467, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "name": ".With()", + "matchedTerms": [], + "startLine": 242, + "endLine": 263, + "sourceBytes": 682, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0ac374940442cf9a4d0a0d3270546ca4b87d036fd9ec4aa8f8e48ca2d49e4407", + "name": ".Group()", + "matchedTerms": [], + "startLine": 268, + "endLine": 274, + "sourceBytes": 106, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:395cabe22892d311ed21d29bf7a01cb9d9d2eb1681c634bfd76121bdcf20b34f", + "name": ".Route()", + "matchedTerms": [], + "startLine": 278, + "endLine": 286, + "sourceBytes": 258, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:29cd93db4ec894393cf257f027b8988a07ee77181191167f3eb2037f2cfdf5be", + "name": ".Mount()", + "matchedTerms": [], + "startLine": 295, + "endLine": 354, + "sourceBytes": 1986, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0fa43499c8e3e06b415a5fd6585b87c3fd541d6c883379d774c9247edeb94951", + "name": ".Routes()", + "matchedTerms": [], + "startLine": 358, + "endLine": 360, + "sourceBytes": 60, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:2e266536f77bb3848784eb117cb5935bce2575bba587e1653ce1e6f9c056ebef", + "name": ".Middlewares()", + "matchedTerms": [], + "startLine": 363, + "endLine": 365, + "sourceBytes": 67, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f0a27beb5e2a6ff88e563c8dbed4dc6b6a697c1895287734f456edcb78d3ada3", + "name": ".Match()", + "matchedTerms": [], + "startLine": 373, + "endLine": 375, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b84d22d8b0ff377822bcdc4cb0d4aaf092970f4a85c5e1107b4d09b1592c015d", + "name": ".Find()", + "matchedTerms": [], + "startLine": 382, + "endLine": 408, + "sourceBytes": 537, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1580e9037451fb2ed515953396059772522a762628fd281119ff95ef6389357a", + "name": ".NotFoundHandler()", + "matchedTerms": [], + "startLine": 412, + "endLine": 417, + "sourceBytes": 138, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6d0c29b15fa70de4383c9685161ae0dea68e92fdbb120770c4a7315e8a2fc10b", + "name": ".MethodNotAllowedHandler()", + "matchedTerms": [], + "startLine": 421, + "endLine": 426, + "sourceBytes": 116, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493 + ] + } + ] + }, + { + "repository": "chi", + "arm": "focused", + "focusTerms": [ + "coordinate", + "explain", + "middleware", + "mux", + "route", + "shar", + "state", + "subrouter" + ], + "sourceBytes": 8000, + "verificationBytesCharged": 8753, + "stdoutBytes": 19718, + "omittedMembers": 5, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 4, + "sourceEvidenceFacts": 4, + "members": [ + { + "id": "sha256:395cabe22892d311ed21d29bf7a01cb9d9d2eb1681c634bfd76121bdcf20b34f", + "name": ".Route()", + "matchedTerms": [ + "route" + ], + "startLine": 278, + "endLine": 286, + "sourceBytes": 258, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0fa43499c8e3e06b415a5fd6585b87c3fd541d6c883379d774c9247edeb94951", + "name": ".Routes()", + "matchedTerms": [ + "route" + ], + "startLine": 358, + "endLine": 360, + "sourceBytes": 60, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:2e266536f77bb3848784eb117cb5935bce2575bba587e1653ce1e6f9c056ebef", + "name": ".Middlewares()", + "matchedTerms": [ + "middleware" + ], + "startLine": 363, + "endLine": 365, + "sourceBytes": 67, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f71b5f8096d259bcc0b17183b18ac62c1c5c4c0927a574f2f2d258824782917f", + "name": ".routeHTTP()", + "matchedTerms": [ + "route" + ], + "startLine": 455, + "endLine": 499, + "sourceBytes": 1075, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c84499b6acb377e9014fa77ee2eddee3dc058a6c36da3e0b4c219cb84435ba47", + "name": ".nextRoutePath()", + "matchedTerms": [ + "route" + ], + "startLine": 501, + "endLine": 508, + "sourceBytes": 297, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:fd923e9ae2c3dec208ca0ee6d8b2033b3d0b175532ec383cfeef661d3f378ea9", + "name": ".updateSubRoutes()", + "matchedTerms": [ + "route" + ], + "startLine": 511, + "endLine": 519, + "sourceBytes": 172, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8527a45e38e92b6d4e24c5284bec8a457fec3c27674c35d8a68d099d885b0807", + "name": ".updateRouteHandler()", + "matchedTerms": [ + "route" + ], + "startLine": 525, + "endLine": 527, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0b03f729c4b4c93626eb48b705704c97e8d051ebb76354fdb1028644c0c003f3", + "name": ".ServeHTTP()", + "matchedTerms": [], + "startLine": 63, + "endLine": 92, + "sourceBytes": 1039, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f592c330a46544e8c614e234af51ff29c320c18c021fc1bc1b5410a58761786b", + "name": ".Use()", + "matchedTerms": [], + "startLine": 100, + "endLine": 105, + "sourceBytes": 225, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:b6f4933c875274b7724a57dc037f278cef17603b5a17c0123f74f09c4859ade3", + "name": ".Handle()", + "matchedTerms": [], + "startLine": 109, + "endLine": 117, + "sourceBytes": 268, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:00488c58ae8e3d2aea91ae763f174cddbcbfa1edb9fda7a9650119778dac1272", + "name": ".HandleFunc()", + "matchedTerms": [], + "startLine": 121, + "endLine": 123, + "sourceBytes": 104, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3456e37dabe75e9c6f4dd179da5eb7f3687aa418b2f40e76eced666aab857948", + "name": ".Method()", + "matchedTerms": [], + "startLine": 127, + "endLine": 133, + "sourceBytes": 233, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:df0333f52e3fd0c8f9492484bcef6287d9044fe0834e27bf8adb31cd646f7bbc", + "name": ".MethodFunc()", + "matchedTerms": [], + "startLine": 137, + "endLine": 139, + "sourceBytes": 120, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c68a79f08a92785f7b855336a541459e9fdc62201d4430b22c5a267720fe0e2e", + "name": ".Connect()", + "matchedTerms": [], + "startLine": 143, + "endLine": 145, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7eaf9ccc10f79e519444ce15ca5e733334b9706658f775085bcd446e28339098", + "name": ".Delete()", + "matchedTerms": [], + "startLine": 149, + "endLine": 151, + "sourceBytes": 109, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:cc338ab0fc91b15feee67ab6ef6b5cb5f32500ccb4011e5a672be9f91a09e728", + "name": ".Get()", + "matchedTerms": [], + "startLine": 155, + "endLine": 157, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f6e8a7560084fdf9a66af0e486fd6ceed76e6607c6ede28fdb1bf8c314ed4a19", + "name": ".Head()", + "matchedTerms": [], + "startLine": 161, + "endLine": 163, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:86384a3e1b4355bef88f940fa46a5af66a9ba09309b1555884da609ceabad4b5", + "name": ".Options()", + "matchedTerms": [], + "startLine": 167, + "endLine": 169, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:940225f9f7c63134e90084b0f543639b43293824ada2130d5aa1c37c6e3de01f", + "name": ".Patch()", + "matchedTerms": [], + "startLine": 173, + "endLine": 175, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:4dc5dca620fc82d5378b46e6646c03877017e141daa1474dab9ce69c1d4cfc56", + "name": ".Post()", + "matchedTerms": [], + "startLine": 179, + "endLine": 181, + "sourceBytes": 105, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:af7652f3b85f23544717c7555bffe88a5b3ee1fc645491c1dd5d6fd3e5367bbe", + "name": ".Put()", + "matchedTerms": [], + "startLine": 185, + "endLine": 187, + "sourceBytes": 103, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e2015cff9c7be8cb29614b5a597580ec714c51b3a9bb8622c8a0c15674b79ade", + "name": ".Query()", + "matchedTerms": [], + "startLine": 191, + "endLine": 193, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8fb9d62ca6fa95670412c09930734152e8175fa59cb9ba6e74c30eba422ed530", + "name": ".Trace()", + "matchedTerms": [], + "startLine": 197, + "endLine": 199, + "sourceBytes": 107, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:eb197ef3e44ebc05271156e9e59ec93db988e2df4ca08efea5788baa91401518", + "name": ".NotFound()", + "matchedTerms": [], + "startLine": 203, + "endLine": 219, + "sourceBytes": 419, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1ac18c4f1a692f25a73fdc98b240ed310b2c4a6cf5065ffde2524dd41b7a1f78", + "name": ".MethodNotAllowed()", + "matchedTerms": [], + "startLine": 223, + "endLine": 239, + "sourceBytes": 467, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:8b2e84e1fc793772014c2d4ab2775681cef7c9e3f005e37905794d12600d4162", + "name": ".With()", + "matchedTerms": [], + "startLine": 242, + "endLine": 263, + "sourceBytes": 682, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:0ac374940442cf9a4d0a0d3270546ca4b87d036fd9ec4aa8f8e48ca2d49e4407", + "name": ".Group()", + "matchedTerms": [], + "startLine": 268, + "endLine": 274, + "sourceBytes": 106, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:29cd93db4ec894393cf257f027b8988a07ee77181191167f3eb2037f2cfdf5be", + "name": ".Mount()", + "matchedTerms": [], + "startLine": 295, + "endLine": 354, + "sourceBytes": 1233, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "chi-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-3", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "chi-4", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "click", + "arm": "baseline", + "focusTerms": [], + "sourceBytes": 874, + "verificationBytesCharged": 874, + "stdoutBytes": 3408, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 3, + "members": [ + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "name": ".__init__()", + "matchedTerms": [], + "startLine": 456, + "endLine": 460, + "sourceBytes": 216, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e878c6438bdce958c03a089d80b07294ed83ae9ee2f0f0a633df5fbf50ae9e52", + "name": ".name()", + "matchedTerms": [], + "startLine": 463, + "endLine": 464, + "sourceBytes": 57, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "name": ".close()", + "matchedTerms": [], + "startLine": 466, + "endLine": 471, + "sourceBytes": 200, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5bb38e0f070dd04a772efc8c9ee16003caa1e687bc2c12cc19b86bef7e08349e", + "name": ".__getattr__()", + "matchedTerms": [], + "startLine": 473, + "endLine": 474, + "sourceBytes": 80, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bc6d909e3fad7e2cbc5726cdd962e543d80d2c22e04f4748a0c4a6a6df90301b", + "name": ".__enter__()", + "matchedTerms": [], + "startLine": 476, + "endLine": 477, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4", + "name": ".__exit__()", + "matchedTerms": [], + "startLine": 479, + "endLine": 485, + "sourceBytes": 211, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1b6882765c798cf51eff1c70453ffc9687e893d38733a758d193bd1e6c7845f1", + "name": ".__repr__()", + "matchedTerms": [], + "startLine": 487, + "endLine": 488, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 455, + 461, + 462 + ] + }, + { + "fact": "click-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "click", + "arm": "unfocused", + "focusTerms": [], + "sourceBytes": 874, + "verificationBytesCharged": 874, + "stdoutBytes": 3408, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 3, + "members": [ + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "name": ".__init__()", + "matchedTerms": [], + "startLine": 456, + "endLine": 460, + "sourceBytes": 216, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e878c6438bdce958c03a089d80b07294ed83ae9ee2f0f0a633df5fbf50ae9e52", + "name": ".name()", + "matchedTerms": [], + "startLine": 463, + "endLine": 464, + "sourceBytes": 57, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "name": ".close()", + "matchedTerms": [], + "startLine": 466, + "endLine": 471, + "sourceBytes": 200, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5bb38e0f070dd04a772efc8c9ee16003caa1e687bc2c12cc19b86bef7e08349e", + "name": ".__getattr__()", + "matchedTerms": [], + "startLine": 473, + "endLine": 474, + "sourceBytes": 80, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bc6d909e3fad7e2cbc5726cdd962e543d80d2c22e04f4748a0c4a6a6df90301b", + "name": ".__enter__()", + "matchedTerms": [], + "startLine": 476, + "endLine": 477, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4", + "name": ".__exit__()", + "matchedTerms": [], + "startLine": 479, + "endLine": 485, + "sourceBytes": 211, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1b6882765c798cf51eff1c70453ffc9687e893d38733a758d193bd1e6c7845f1", + "name": ".__repr__()", + "matchedTerms": [], + "startLine": 487, + "endLine": 488, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 455, + 461, + 462 + ] + }, + { + "fact": "click-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "click", + "arm": "focused", + "focusTerms": [ + "_atomic", + "behavior", + "cleanup", + "close", + "context", + "exception", + "explain", + "file", + "manager", + "ownership" + ], + "sourceBytes": 874, + "verificationBytesCharged": 874, + "stdoutBytes": 3771, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 3, + "members": [ + { + "id": "sha256:1e4e32dfcdfe036023e5c263eaba38bfe92eb391597b74fc5d70ab50a877cfa3", + "name": ".close()", + "matchedTerms": [ + "close" + ], + "startLine": 466, + "endLine": 471, + "sourceBytes": 200, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:f2973c383f7639367620131c87a5de0b9e0845a87f6dadebdf32160c1463a936", + "name": ".__init__()", + "matchedTerms": [], + "startLine": 456, + "endLine": 460, + "sourceBytes": 216, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:e878c6438bdce958c03a089d80b07294ed83ae9ee2f0f0a633df5fbf50ae9e52", + "name": ".name()", + "matchedTerms": [], + "startLine": 463, + "endLine": 464, + "sourceBytes": 57, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5bb38e0f070dd04a772efc8c9ee16003caa1e687bc2c12cc19b86bef7e08349e", + "name": ".__getattr__()", + "matchedTerms": [], + "startLine": 473, + "endLine": 474, + "sourceBytes": 80, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bc6d909e3fad7e2cbc5726cdd962e543d80d2c22e04f4748a0c4a6a6df90301b", + "name": ".__enter__()", + "matchedTerms": [], + "startLine": 476, + "endLine": 477, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7bf788bd87c967bb2c80b682108ca265ce1ea9c213b3c8d55931a267d73c15b4", + "name": ".__exit__()", + "matchedTerms": [], + "startLine": 479, + "endLine": 485, + "sourceBytes": 211, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:1b6882765c798cf51eff1c70453ffc9687e893d38733a758d193bd1e6c7845f1", + "name": ".__repr__()", + "matchedTerms": [], + "startLine": 487, + "endLine": 488, + "sourceBytes": 55, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "click-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 455, + 461, + 462 + ] + }, + { + "fact": "click-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "click-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "jsoup", + "arm": "baseline", + "focusTerms": [], + "sourceBytes": 6208, + "verificationBytesCharged": 6208, + "stdoutBytes": 12866, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 4, + "members": [ + { + "id": "sha256:66aaadb3479119cae227c0e51b5674eae36cb9b29e80428e2697aa7cb45518f0", + "name": "", + "matchedTerms": [], + "startLine": 50, + "endLine": 53, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "name": ".clean()", + "matchedTerms": [], + "startLine": 62, + "endLine": 70, + "sourceBytes": 319, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:817144c6469132780623c859650f3dbb59eef49b75108e12e8fd2efe7763b330", + "name": ".isValid()", + "matchedTerms": [], + "startLine": 94, + "endLine": 101, + "sourceBytes": 441, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "name": ".isValidBodyHtml()", + "matchedTerms": [], + "startLine": 124, + "endLine": 133, + "sourceBytes": 630, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d82578355e71b0d55f68499706f70276db9cf44a16bf283bba6360df410deec5", + "name": "", + "matchedTerms": [], + "startLine": 143, + "endLine": 146, + "sourceBytes": 144, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:65912eb3660f15092355589775d874a7a702efcf0336e9865cf93b4fb56aaa29", + "name": ".head()", + "matchedTerms": [], + "startLine": 148, + "endLine": 173, + "sourceBytes": 1371, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:fb8588d79c5d9dcbd1a0c6a8e2f901d5c54be3050e7626a3b8f59a8c55bd025c", + "name": ".tail()", + "matchedTerms": [], + "startLine": 175, + "endLine": 179, + "sourceBytes": 266, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "name": ".copySafeNodes()", + "matchedTerms": [], + "startLine": 182, + "endLine": 186, + "sourceBytes": 227, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7ce3db44fb899d894dd900ab8808bd0fdc411323a6276f4164fede39e46f8157", + "name": ".createSafeElement()", + "matchedTerms": [], + "startLine": 188, + "endLine": 235, + "sourceBytes": 2553, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bd66755ef9e0f349f3e772c8d9b23a0441194bd6300a6431d51bc2f11650e890", + "name": "", + "matchedTerms": [], + "startLine": 241, + "endLine": 244, + "sourceBytes": 146, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "jsoup", + "arm": "unfocused", + "focusTerms": [], + "sourceBytes": 6208, + "verificationBytesCharged": 6208, + "stdoutBytes": 12866, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 4, + "members": [ + { + "id": "sha256:66aaadb3479119cae227c0e51b5674eae36cb9b29e80428e2697aa7cb45518f0", + "name": "", + "matchedTerms": [], + "startLine": 50, + "endLine": 53, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "name": ".clean()", + "matchedTerms": [], + "startLine": 62, + "endLine": 70, + "sourceBytes": 319, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:817144c6469132780623c859650f3dbb59eef49b75108e12e8fd2efe7763b330", + "name": ".isValid()", + "matchedTerms": [], + "startLine": 94, + "endLine": 101, + "sourceBytes": 441, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "name": ".isValidBodyHtml()", + "matchedTerms": [], + "startLine": 124, + "endLine": 133, + "sourceBytes": 630, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d82578355e71b0d55f68499706f70276db9cf44a16bf283bba6360df410deec5", + "name": "", + "matchedTerms": [], + "startLine": 143, + "endLine": 146, + "sourceBytes": 144, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:65912eb3660f15092355589775d874a7a702efcf0336e9865cf93b4fb56aaa29", + "name": ".head()", + "matchedTerms": [], + "startLine": 148, + "endLine": 173, + "sourceBytes": 1371, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:fb8588d79c5d9dcbd1a0c6a8e2f901d5c54be3050e7626a3b8f59a8c55bd025c", + "name": ".tail()", + "matchedTerms": [], + "startLine": 175, + "endLine": 179, + "sourceBytes": 266, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "name": ".copySafeNodes()", + "matchedTerms": [], + "startLine": 182, + "endLine": 186, + "sourceBytes": 227, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7ce3db44fb899d894dd900ab8808bd0fdc411323a6276f4164fede39e46f8157", + "name": ".createSafeElement()", + "matchedTerms": [], + "startLine": 188, + "endLine": 235, + "sourceBytes": 2553, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bd66755ef9e0f349f3e772c8d9b23a0441194bd6300a6431d51bc2f11650e890", + "name": "", + "matchedTerms": [], + "startLine": 241, + "endLine": 244, + "sourceBytes": 146, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "jsoup", + "arm": "focused", + "focusTerms": [ + "clean", + "cleaner", + "document", + "explain", + "input", + "mutate", + "responsibility", + "safelist", + "supply", + "us", + "whether" + ], + "sourceBytes": 6208, + "verificationBytesCharged": 6208, + "stdoutBytes": 13309, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 0, + "sourceEvidenceFacts": 4, + "members": [ + { + "id": "sha256:d490451c6bf56501db666f0a6c3c143f1a6d8a90120632415cc739cbf1a8f034", + "name": ".clean()", + "matchedTerms": [ + "clean" + ], + "startLine": 62, + "endLine": 70, + "sourceBytes": 319, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:66aaadb3479119cae227c0e51b5674eae36cb9b29e80428e2697aa7cb45518f0", + "name": "", + "matchedTerms": [], + "startLine": 50, + "endLine": 53, + "sourceBytes": 111, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:817144c6469132780623c859650f3dbb59eef49b75108e12e8fd2efe7763b330", + "name": ".isValid()", + "matchedTerms": [], + "startLine": 94, + "endLine": 101, + "sourceBytes": 441, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7a0cdc660eb47dd50d176901e606343fb9b0147d56440a33acc4f3ef5811d406", + "name": ".isValidBodyHtml()", + "matchedTerms": [], + "startLine": 124, + "endLine": 133, + "sourceBytes": 630, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:d82578355e71b0d55f68499706f70276db9cf44a16bf283bba6360df410deec5", + "name": "", + "matchedTerms": [], + "startLine": 143, + "endLine": 146, + "sourceBytes": 144, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:65912eb3660f15092355589775d874a7a702efcf0336e9865cf93b4fb56aaa29", + "name": ".head()", + "matchedTerms": [], + "startLine": 148, + "endLine": 173, + "sourceBytes": 1371, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:fb8588d79c5d9dcbd1a0c6a8e2f901d5c54be3050e7626a3b8f59a8c55bd025c", + "name": ".tail()", + "matchedTerms": [], + "startLine": 175, + "endLine": 179, + "sourceBytes": 266, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c980a14050d13e4546d2b8727dc5881230db3b86bd14b462eaf938052c2f80f3", + "name": ".copySafeNodes()", + "matchedTerms": [], + "startLine": 182, + "endLine": 186, + "sourceBytes": 227, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:7ce3db44fb899d894dd900ab8808bd0fdc411323a6276f4164fede39e46f8157", + "name": ".createSafeElement()", + "matchedTerms": [], + "startLine": 188, + "endLine": 235, + "sourceBytes": 2553, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:bd66755ef9e0f349f3e772c8d9b23a0441194bd6300a6431d51bc2f11650e890", + "name": "", + "matchedTerms": [], + "startLine": 241, + "endLine": 244, + "sourceBytes": 146, + "truncated": false, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "jsoup-1", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "jsoup-4", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + } + ] + }, + { + "repository": "redux", + "arm": "baseline", + "focusTerms": [], + "sourceBytes": 5047, + "verificationBytesCharged": 5047, + "stdoutBytes": 10329, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:692fe8cbc7b8e5a03bcb79c20531aefdbfa31098ea43bbe9b0301d61cd869f27", + "name": "ensureCanMutateNextListeners()", + "matchedTerms": [], + "startLine": 152, + "endLine": 159, + "sourceBytes": 231, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:ac32c352aac7217b9b481c36ebdbd1304d84593968416c35058a0445860f30e2", + "name": "getState()", + "matchedTerms": [], + "startLine": 166, + "endLine": 176, + "sourceBytes": 357, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:f27566baebab97a78ea7b6d143149ecdff76b39167fb12b42612a3e69fba5a5d", + "name": "subscribe()", + "matchedTerms": [], + "startLine": 201, + "endLine": 243, + "sourceBytes": 1287, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:ee27944e9b61e75b17b685fb67d51cccd459621e8ed9db30b18f5c7b02094612", + "name": "dispatch()", + "matchedTerms": [], + "startLine": 270, + "endLine": 309, + "sourceBytes": 1378, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:32f1ead88c324d96b4c7dd1db2d0692f3c7fb04a33e23d485f4b2ad5ea62b6a4", + "name": "replaceReducer()", + "matchedTerms": [], + "startLine": 320, + "endLine": 336, + "sourceBytes": 654, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:5afb3e08978d3232121b2cf67a14f748a5e6039c4a5256a2cf64a30473551eea", + "name": "observable()", + "matchedTerms": [], + "startLine": 344, + "endLine": 380, + "sourceBytes": 1140, + "truncated": false, + "digestVerified": false + } + ], + "facts": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134 + ] + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394 + ] + } + ] + }, + { + "repository": "redux", + "arm": "unfocused", + "focusTerms": [], + "sourceBytes": 5047, + "verificationBytesCharged": 5047, + "stdoutBytes": 10329, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:692fe8cbc7b8e5a03bcb79c20531aefdbfa31098ea43bbe9b0301d61cd869f27", + "name": "ensureCanMutateNextListeners()", + "matchedTerms": [], + "startLine": 152, + "endLine": 159, + "sourceBytes": 231, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:ac32c352aac7217b9b481c36ebdbd1304d84593968416c35058a0445860f30e2", + "name": "getState()", + "matchedTerms": [], + "startLine": 166, + "endLine": 176, + "sourceBytes": 357, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:f27566baebab97a78ea7b6d143149ecdff76b39167fb12b42612a3e69fba5a5d", + "name": "subscribe()", + "matchedTerms": [], + "startLine": 201, + "endLine": 243, + "sourceBytes": 1287, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:ee27944e9b61e75b17b685fb67d51cccd459621e8ed9db30b18f5c7b02094612", + "name": "dispatch()", + "matchedTerms": [], + "startLine": 270, + "endLine": 309, + "sourceBytes": 1378, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:32f1ead88c324d96b4c7dd1db2d0692f3c7fb04a33e23d485f4b2ad5ea62b6a4", + "name": "replaceReducer()", + "matchedTerms": [], + "startLine": 320, + "endLine": 336, + "sourceBytes": 654, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:5afb3e08978d3232121b2cf67a14f748a5e6039c4a5256a2cf64a30473551eea", + "name": "observable()", + "matchedTerms": [], + "startLine": 344, + "endLine": 380, + "sourceBytes": 1140, + "truncated": false, + "digestVerified": false + } + ], + "facts": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134 + ] + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394 + ] + } + ] + }, + { + "repository": "redux", + "arm": "focused", + "focusTerms": [ + "create", + "dispatch", + "explain", + "responsibility", + "safely", + "share", + "state", + "store", + "subscription" + ], + "sourceBytes": 5047, + "verificationBytesCharged": 5047, + "stdoutBytes": 10673, + "omittedMembers": 0, + "unavailableMembers": 0, + "truncated": false, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:ac32c352aac7217b9b481c36ebdbd1304d84593968416c35058a0445860f30e2", + "name": "getState()", + "matchedTerms": [ + "state" + ], + "startLine": 166, + "endLine": 176, + "sourceBytes": 357, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:ee27944e9b61e75b17b685fb67d51cccd459621e8ed9db30b18f5c7b02094612", + "name": "dispatch()", + "matchedTerms": [ + "dispatch" + ], + "startLine": 270, + "endLine": 309, + "sourceBytes": 1378, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:692fe8cbc7b8e5a03bcb79c20531aefdbfa31098ea43bbe9b0301d61cd869f27", + "name": "ensureCanMutateNextListeners()", + "matchedTerms": [], + "startLine": 152, + "endLine": 159, + "sourceBytes": 231, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:f27566baebab97a78ea7b6d143149ecdff76b39167fb12b42612a3e69fba5a5d", + "name": "subscribe()", + "matchedTerms": [], + "startLine": 201, + "endLine": 243, + "sourceBytes": 1287, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:32f1ead88c324d96b4c7dd1db2d0692f3c7fb04a33e23d485f4b2ad5ea62b6a4", + "name": "replaceReducer()", + "matchedTerms": [], + "startLine": 320, + "endLine": 336, + "sourceBytes": 654, + "truncated": false, + "digestVerified": false + }, + { + "id": "sha256:5afb3e08978d3232121b2cf67a14f748a5e6039c4a5256a2cf64a30473551eea", + "name": "observable()", + "matchedTerms": [], + "startLine": 344, + "endLine": 380, + "sourceBytes": 1140, + "truncated": false, + "digestVerified": false + } + ], + "facts": [ + { + "fact": "redux-1", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134 + ] + }, + { + "fact": "redux-2", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-3", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "redux-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394 + ] + } + ] + }, + { + "repository": "walkdir", + "arm": "baseline", + "focusTerms": [], + "sourceBytes": 8000, + "verificationBytesCharged": 8405, + "stdoutBytes": 13045, + "omittedMembers": 2, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:5c1a2f2ab2725e97f0e936e554e3381bc72abac584341846089152518b8314b1", + "name": ".next()", + "matchedTerms": [], + "startLine": 687, + "endLine": 734, + "sourceBytes": 1812, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c3fe8fc9e9d0038d0c1191e7cf0d4ee7783cebd55c755a07c627023b60a3d188", + "name": ".skip_current_dir()", + "matchedTerms": [], + "startLine": 781, + "endLine": 785, + "sourceBytes": 117, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5704b7023e55533acf5164ddd3ae50467aea1abf4c19866f3e143ae7bf09d370", + "name": ".filter_entry()", + "matchedTerms": [], + "startLine": 833, + "endLine": 838, + "sourceBytes": 169, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "name": ".handle_entry()", + "matchedTerms": [], + "startLine": 840, + "endLine": 882, + "sourceBytes": 1726, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "name": ".get_deferred_dir()", + "matchedTerms": [], + "startLine": 884, + "endLine": 899, + "sourceBytes": 607, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "name": ".push()", + "matchedTerms": [], + "startLine": 901, + "endLine": 948, + "sourceBytes": 2459, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3d843599c82e0cf88d7512278cc2b4f2ffc87c5797785e0c9caad89ab4af8f98", + "name": ".pop()", + "matchedTerms": [], + "startLine": 950, + "endLine": 959, + "sourceBytes": 484, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "name": ".follow()", + "matchedTerms": [], + "startLine": 961, + "endLine": 971, + "sourceBytes": 431, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "name": ".check_loop()", + "matchedTerms": [], + "startLine": 973, + "endLine": 989, + "sourceBytes": 195, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 972, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989 + ] + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 883 + ] + } + ] + }, + { + "repository": "walkdir", + "arm": "unfocused", + "focusTerms": [], + "sourceBytes": 8000, + "verificationBytesCharged": 8405, + "stdoutBytes": 13045, + "omittedMembers": 2, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:5c1a2f2ab2725e97f0e936e554e3381bc72abac584341846089152518b8314b1", + "name": ".next()", + "matchedTerms": [], + "startLine": 687, + "endLine": 734, + "sourceBytes": 1812, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c3fe8fc9e9d0038d0c1191e7cf0d4ee7783cebd55c755a07c627023b60a3d188", + "name": ".skip_current_dir()", + "matchedTerms": [], + "startLine": 781, + "endLine": 785, + "sourceBytes": 117, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5704b7023e55533acf5164ddd3ae50467aea1abf4c19866f3e143ae7bf09d370", + "name": ".filter_entry()", + "matchedTerms": [], + "startLine": 833, + "endLine": 838, + "sourceBytes": 169, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "name": ".handle_entry()", + "matchedTerms": [], + "startLine": 840, + "endLine": 882, + "sourceBytes": 1726, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "name": ".get_deferred_dir()", + "matchedTerms": [], + "startLine": 884, + "endLine": 899, + "sourceBytes": 607, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "name": ".push()", + "matchedTerms": [], + "startLine": 901, + "endLine": 948, + "sourceBytes": 2459, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3d843599c82e0cf88d7512278cc2b4f2ffc87c5797785e0c9caad89ab4af8f98", + "name": ".pop()", + "matchedTerms": [], + "startLine": 950, + "endLine": 959, + "sourceBytes": 484, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "name": ".follow()", + "matchedTerms": [], + "startLine": 961, + "endLine": 971, + "sourceBytes": 431, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "name": ".check_loop()", + "matchedTerms": [], + "startLine": 973, + "endLine": 989, + "sourceBytes": 195, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 972, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989 + ] + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 883 + ] + } + ] + }, + { + "repository": "walkdir", + "arm": "focused", + "focusTerms": [ + "detect", + "directory", + "explain", + "iter", + "limit", + "loop", + "open", + "responsibility", + "symlink" + ], + "sourceBytes": 8000, + "verificationBytesCharged": 8405, + "stdoutBytes": 13471, + "omittedMembers": 2, + "unavailableMembers": 0, + "truncated": true, + "literalFacts": 1, + "sourceEvidenceFacts": 2, + "members": [ + { + "id": "sha256:43858f600e266290da4e20870d1b0764d496ccbe59300e846a1675e943ec53b3", + "name": ".check_loop()", + "matchedTerms": [ + "loop" + ], + "startLine": 973, + "endLine": 989, + "sourceBytes": 600, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5c1a2f2ab2725e97f0e936e554e3381bc72abac584341846089152518b8314b1", + "name": ".next()", + "matchedTerms": [], + "startLine": 687, + "endLine": 734, + "sourceBytes": 1812, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c3fe8fc9e9d0038d0c1191e7cf0d4ee7783cebd55c755a07c627023b60a3d188", + "name": ".skip_current_dir()", + "matchedTerms": [], + "startLine": 781, + "endLine": 785, + "sourceBytes": 117, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:5704b7023e55533acf5164ddd3ae50467aea1abf4c19866f3e143ae7bf09d370", + "name": ".filter_entry()", + "matchedTerms": [], + "startLine": 833, + "endLine": 838, + "sourceBytes": 169, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:6157f09ad96d7be5e50367736300c88ecb6e1b6a992282d57aeabd7e5c0bc21e", + "name": ".handle_entry()", + "matchedTerms": [], + "startLine": 840, + "endLine": 882, + "sourceBytes": 1726, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:219c38f772358d6f0cd42cd06b879fe706b6672b0deee9461f8f7ec785618867", + "name": ".get_deferred_dir()", + "matchedTerms": [], + "startLine": 884, + "endLine": 899, + "sourceBytes": 607, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:c4870d899db4c0d3e82cf28916d9cadef5b8d7b249ea6fe7aa97bc7d465dfe45", + "name": ".push()", + "matchedTerms": [], + "startLine": 901, + "endLine": 948, + "sourceBytes": 2459, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:3d843599c82e0cf88d7512278cc2b4f2ffc87c5797785e0c9caad89ab4af8f98", + "name": ".pop()", + "matchedTerms": [], + "startLine": 950, + "endLine": 959, + "sourceBytes": 484, + "truncated": false, + "digestVerified": true + }, + { + "id": "sha256:573323364aaddd351eb7ffdf1c271a5f20df53899d8d75e53cd6f85eac3dde78", + "name": ".follow()", + "matchedTerms": [], + "startLine": 961, + "endLine": 971, + "sourceBytes": 26, + "truncated": true, + "digestVerified": true + } + ], + "facts": [ + { + "fact": "walkdir-1", + "literalWitnessCoverage": true, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-2", + "literalWitnessCoverage": false, + "sourceEvidence": true, + "missingLines": [] + }, + { + "fact": "walkdir-3", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972 + ] + }, + { + "fact": "walkdir-4", + "literalWitnessCoverage": false, + "sourceEvidence": false, + "missingLines": [ + 883 + ] + } + ] + } + ], + "comparison": "Compass native member-source before/after only. The separate paired public-neighbor 8000-byte control remains Compass 14/20 versus Graphify 15/20. Do not equate it with the native-member totals or claim a Graphify focus comparison.", + "changes": { + "gainedFacts": [ + "chi-4" + ], + "lostFacts": [], + "unchangedUnfocusedStdoutAndStderr": 5, + "unchangedSourceBytes": true, + "extraStdoutBytes": 2436, + "extraVerificationBytesCharged": 651 + }, + "remaining": [ + "Click ownership/name fact lacks the class header and @property decorator outside recorded callable spans. No class-header allowance is applied.", + "Redux enhancer delegation and complete store/observable API are not fully within callable member evidence. Six returned Redux source excerpts have no recorded digest in both arms; output correctly labels them unverified. The external audit checks current pinned source without upgrading native provenance.", + "WalkDir still lacks complete symlink-loop and contents-first witnesses. Prioritizing check_loop does not by itself retrieve all its surrounding call-path evidence.", + "Name focus is a lexical heuristic, not semantic entailment or a completeness guarantee for natural-language questions. Broader or held-out questions can regress." + ], + "validation": { + "complete": true, + "sourceSha256": { + "CHANGELOG.md": "9dc1d9ab982aec9dc8fe18a0a097d25f6556d695bbdbe0542d1c1ce0997f3c5e", + "COMPATIBILITY.md": "20a82fa52684237608e320173141573b1649da5f30006b86b5f720d4d4a6420c", + "crates/compass-cli/src/help.rs": "2fde352a44872278408173f11ffeb9a693453724f0e268f453e73b9657cf200f", + "crates/compass-cli/src/lib.rs": "ca2d616acc7536df7569783ae40cde86a0940412ae3a9edd5a8100585ad7a7d3", + "crates/compass-cli/tests/code_query_cli.rs": "b8f0c339dcb1db197fa5f5589763c9279d02f025108a684ba7bf9955ec399e63", + "crates/compass-query/src/explanation_members.rs": "ce7dedd242f7b44c724ba11f914aed58a4cfdffdbbcdb91cf158c3e2c19b9927", + "crates/compass-query/src/lib.rs": "f0c59af96732ab0c35f577043c3941c57c138ed0e13efe96f0b8d039d5cc6f8d", + "crates/compass-query/tests/explanation_members.rs": "a2897b9a609be0c023830ee8c5f45f88e2695d91f18684075216040a902b0d06", + "docs/reference/outputs.md": "4f4612efdb08d5e28f2f628084b908df652660e2149c0575340356bc16e26a13" + }, + "steps": [ + { + "name": "fmt", + "argv": [ + "cargo", + "fmt", + "--all", + "--", + "--check" + ], + "exitCode": 0, + "seconds": 3.6076712920330465 + }, + { + "name": "query", + "argv": [ + "cargo", + "test", + "-p", + "compass-query", + "--test", + "explanation_members", + "--test", + "explanation_source", + "--locked" + ], + "exitCode": 0, + "seconds": 23.21706162497867 + }, + { + "name": "cli", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "code_query_cli", + "--locked" + ], + "exitCode": 0, + "seconds": 125.1199858749751 + }, + { + "name": "clippy", + "argv": [ + "cargo", + "clippy", + "--workspace", + "--lib", + "--bins", + "--locked", + "--", + "-D", + "warnings" + ], + "exitCode": 0, + "seconds": 45.434337500017136 + }, + { + "name": "workspace-tests", + "argv": [ + "cargo", + "test", + "--workspace", + "--lib", + "--bins", + "--locked" + ], + "exitCode": 0, + "seconds": 125.17055554199032 + }, + { + "name": "product", + "argv": [ + "cargo", + "test", + "-p", + "compass-cli", + "--test", + "compass_product", + "--locked" + ], + "exitCode": 0, + "seconds": 15.18113749998156 + }, + { + "name": "boundary", + "argv": [ + "sh", + "scripts/check_product_boundary.sh" + ], + "exitCode": 0, + "seconds": 0.04564858297817409 + }, + { + "name": "build", + "argv": [ + "cargo", + "build", + "-p", + "compass-cli", + "--bin", + "compass", + "--locked" + ], + "exitCode": 0, + "seconds": 3.6144565829308704 + } + ], + "binarySha256": "e33934b94d1e254ba59906fddece098d8433bf7e31af746e46aac3dfc6744f97" + }, + "testCounts": { + "query": { + "passed": 19, + "failed": 0, + "ignored": 0 + }, + "cli": { + "passed": 42, + "failed": 0, + "ignored": 0 + }, + "workspace-tests": { + "passed": 1106, + "failed": 0, + "ignored": 2 + }, + "product": { + "passed": 9, + "failed": 0, + "ignored": 0 + } + }, + "benchmarkTests": 187, + "replay": { + "exitCode": 0, + "byteIdentical": true, + "verifiedSummarySha256": "cc397fe9a825189f9678e8a7b0167ff544fc250a1294af649d4327b297d578ae" + }, + "attempts": [ + "Round 01 failed to compile because the graph runtime NodeRecord exposes attributes rather than a public name field. Corrected to its existing string accessor.", + "Inspection also required the existing lexical index convention of retaining snake-case identifiers and adding their components, since identifier_tokens itself preserves underscores. No experiment scores informed this correction.", + "The initial real-source verifier assumed every member had a digest. Redux exposes six correctly labeled unverified members; the verifier now checks both statuses and retains the six missing-digest cases. Original verifier and failure log are preserved." + ], + "limitations": [ + "Previously inspected five-language development subjects and source witnesses, not held-out evidence.", + "Separate same-agent verifier independently checks ordering, literal source, provenance and scoring; no independent human semantic adjudication.", + "Indentation normalization permits source evidence when the recorded span starts at a declaration token; literal totals remain separate.", + "Graphify has no recorded equivalent native flag. No new paired superiority claim is made.", + "This adds an optional lexical ordering; it does not synthesize explanations, prove god-object defects, improve extraction or establish path/community quality.", + "JavaScript, browser, full platform, packaging and extraction fixture qualification were not rerun because these production surfaces are unchanged." + ], + "artifacts": { + "member-focus-01/fmt.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-01/query.log": "3adccd3daa7a924f3dacf82161db3e064c28aaa3260ed1d61f8cc8cdb6cd66af", + "member-focus-01/validate.py": "f53d716f5193cf59ace12193efa0b79cbe6dc3e49efb233396cfe0a3425bf616", + "member-focus-01/validation.json": "4fb1ecaba056176fbca23f66add3b158b23772284bb492ab3e9c588d7661f07e", + "member-focus-01/validation.log": "e0c517a18d1dde4c86693ba272c3363e30348cae387d839b3134c002c63c3ec6", + "member-focus-02/benchmark-tests.log": "f350f703f21eac7462f3f181aeb1ff015d2adc50583af1604011b84f4039da8f", + "member-focus-02/boundary.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/build.log": "77a964cd95b5d11d0c62ba003bf7c011ec878fb3d764793073654e9706249374", + "member-focus-02/capture/capture.json": "9429bc124b14c2d697e9990ca79899de5f9b044e0cb0967b8a961139f510ec48", + "member-focus-02/capture/capture.py": "881c5438d17db97bcf77c349267daca5ddcffaa9574bad8f90592a2437ec2905", + "member-focus-02/capture/explanation_focus_registration.json": "8287faea7015f865cbea8b5088ef20ca04d10ee1f367724327ac02ce3fa80cc6", + "member-focus-02/capture/explanation_members.rs": "ce7dedd242f7b44c724ba11f914aed58a4cfdffdbbcdb91cf158c3e2c19b9927", + "member-focus-02/capture/lexical.rs": "145f751ba0755c29adbb6498d206fbf729aa62a9559ad58d256a05b3e43f50ee", + "member-focus-02/capture/raw/chi/baseline.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/chi/baseline.stdout": "a827aea6e1c28e363d972da58943349455dd43154d162d5adecfffaaf2bcd41f", + "member-focus-02/capture/raw/chi/focused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/chi/focused.stdout": "97377fc95987e0e976d369cfb8a77d75bdd15d6c131fdffc1c7460c1250c793b", + "member-focus-02/capture/raw/chi/unfocused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/chi/unfocused.stdout": "a827aea6e1c28e363d972da58943349455dd43154d162d5adecfffaaf2bcd41f", + "member-focus-02/capture/raw/click/baseline.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/click/baseline.stdout": "c12fa7fd7a03bcdf46578c982de39a5d539d41846e69dd3a93bd5a8b59bfcba2", + "member-focus-02/capture/raw/click/focused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/click/focused.stdout": "b104435f28a6569dc47ef30aa624f46dec11d93309d15d7c06857f25db11f5fb", + "member-focus-02/capture/raw/click/unfocused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/click/unfocused.stdout": "c12fa7fd7a03bcdf46578c982de39a5d539d41846e69dd3a93bd5a8b59bfcba2", + "member-focus-02/capture/raw/jsoup/baseline.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/jsoup/baseline.stdout": "d0fa6a20d92ec2792e13ba6e7560c2bf0b8056a783238b095156fefb75dff88c", + "member-focus-02/capture/raw/jsoup/focused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/jsoup/focused.stdout": "07c72538d2cb1144da311e8e396a48d4cf62fd5bbe4c9470fec8fbe3e15a9a25", + "member-focus-02/capture/raw/jsoup/unfocused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/jsoup/unfocused.stdout": "d0fa6a20d92ec2792e13ba6e7560c2bf0b8056a783238b095156fefb75dff88c", + "member-focus-02/capture/raw/redux/baseline.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/redux/baseline.stdout": "09cae94ec07ddd8ae7e792231f0d16573651bae7698a307a454064d48b2db35b", + "member-focus-02/capture/raw/redux/focused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/redux/focused.stdout": "727e9a271cdee034c631b5faa71892da159dfe3bbd611024a9fa8bb5db22fd63", + "member-focus-02/capture/raw/redux/unfocused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/redux/unfocused.stdout": "09cae94ec07ddd8ae7e792231f0d16573651bae7698a307a454064d48b2db35b", + "member-focus-02/capture/raw/walkdir/baseline.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/walkdir/baseline.stdout": "9457c5572e2d628f72669b37702d9c25ad9368e5431cb54bfcb7d2ce1a4f9459", + "member-focus-02/capture/raw/walkdir/focused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/walkdir/focused.stdout": "564d5ec339b8cce15de47828f1b0f001818e17d69c05678daff4921317f68925", + "member-focus-02/capture/raw/walkdir/unfocused.stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/capture/raw/walkdir/unfocused.stdout": "9457c5572e2d628f72669b37702d9c25ad9368e5431cb54bfcb7d2ce1a4f9459", + "member-focus-02/capture/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "member-focus-02/capture/text.rs": "4df8680d3af04cc8be70c6f4279fda6a928b83c1a732d3d0f43132c7cf05e177", + "member-focus-02/capture.log": "b466a4a18114007fefe8bb8dd35d87d3daf0bb8606dac897c1b110fd8b063e9d", + "member-focus-02/capture.py": "881c5438d17db97bcf77c349267daca5ddcffaa9574bad8f90592a2437ec2905", + "member-focus-02/cli.log": "a0cd18279e48a3ff75e62d26fdae71d05e105664de0aef8754d49e9239728049", + "member-focus-02/clippy.log": "3702ebc838cdc4b12c75dd08bd3e94438ca071747259f0f9d35279093531c083", + "member-focus-02/fmt.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "member-focus-02/product.log": "e610f15bfe18551483b5a9558502fcda1b66c3ed3d11db340bd7319f29804931", + "member-focus-02/query.log": "f3615b2a40ca586da9789e51e6c5a5130bf4917d228f98e85bb0b6c83ac36029", + "member-focus-02/repeat-verification.log": "5bffc34b4de117f0c44e4da27e27e1a4df873534e4c4cd2532290438e333f9ad", + "member-focus-02/replay.json": "237c3b095a5f92f54fda4e7f2337cb4439762971bdd9773b192dd3ff87ea0ead", + "member-focus-02/validate.py": "f53d716f5193cf59ace12193efa0b79cbe6dc3e49efb233396cfe0a3425bf616", + "member-focus-02/validation.json": "a6ccd56bb1d0fae365623e21d5a0eb69ed13b868ce0bf3ac73b149bc7ea551d9", + "member-focus-02/validation.log": "f15fb96a5c9b73738583adfaab96077850ac08ce3db9ba0a297770526f5d52c8", + "member-focus-02/verification-attempt-01.log": "fae002c6186bb6c9c4bb993a213acbb53155683392bce278313874f7c970f615", + "member-focus-02/verification.log": "5bffc34b4de117f0c44e4da27e27e1a4df873534e4c4cd2532290438e333f9ad", + "member-focus-02/verified-summary.json": "cc397fe9a825189f9678e8a7b0167ff544fc250a1294af649d4327b297d578ae", + "member-focus-02/verify-attempt-01.py": "016fa550ec325a23e55601b7ad43a0022b0ab9b3180a8090c78fe6b46ae57239", + "member-focus-02/verify.py": "a3ddabd6c2d354012aa8ab99fffd5130199b4abda0d54910311c5bd155ca4bc3", + "member-focus-02/workspace-tests.log": "f96af4283a25ab2adadf1b80b460e938a97950bed8f7c875d6dddbff3e2b859f", + "member-focus-02/write_review.py": "3b58fea9f1ac1e189b79e3c529789172cb3657facdfdadf23590f47d9b15da79" + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 1f94045c8..7ddcfbb49 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -3218,6 +3218,89 @@ then evaluate authored answers under a separately registered common workflow. God-object defect judgments, longer-walk quality, functional community quality and held-out confirmation remain open. +## Optional member-name focus: bounded native explanation improvement + +Registration `1c80747a` fixes the existing five questions, source pins, exact +root identities, graphs and 8,000-byte source quota before this experiment. +Production commit `293582c3` adds `explain --source-members --member-focus TEXT`. +It uses normalized query terms to count distinct matches in recorded callable +names, prioritizes higher counts, and uses the existing source order for ties. +Snake-case names retain their whole token and components, following the existing +lexical index. No source text is read to rank. Unmatched members remain eligible; +focus neither selects an ambiguous owner nor changes membership or source bounds. +The output discloses normalized terms and the matches behind each retained member. + +The query library owns ranking and validation; the CLI parses and presents it. +Focus accepts at most 4,096 bytes and 32 distinct searchable terms. Empty or +unsearchable input, repeated options and focus without member mode fail explicitly. +All existing discovery, metadata, nesting, source-verification and retained-byte +limits remain in force. Missing digests and failed source reads keep their +individual statuses. Without focus, all five captured stdout and stderr streams +are byte-identical to the frozen baseline executable. + +The experiment passes each **full original question** as focus, without manually +choosing helpful method names or tuning individual questions after capture. + +| Native member evidence, 8,000-byte quota | Without focus | Full-question focus | +| --- | ---: | ---: | +| Chi | 3/4 | 4/4 | +| Click | 3/4 | 3/4 | +| jsoup | 4/4 | 4/4 | +| Redux | 2/4 | 2/4 | +| WalkDir | 2/4 | 2/4 | +| Total | 14/20 | 15/20 | + +There is one gain, `chi-4`, and no lost facts on this panel. Name matches bring +`routeHTTP` and related routing methods ahead of earlier methods without matching names, +so the routing witness fits. Literal coverage, before indentation normalization, +is 5/20 versus 6/20. Recorded callable spans can start at a declaration token +rather than its leading indentation; the independent verifier checks each +returned span against pinned source before normalized evidence scoring. +No missing-class-header allowance is applied to this native arm. + +Both arms retain 28,129 total source bytes. Focus increases stdout from 58,506 +to 60,942 bytes and charged full-span verification work from 28,636 to 29,287 +bytes. Name matches are a heuristic, not behavioral evidence or a general quality +guarantee. Broad or held-out questions may regress even though this panel did not. + +Remaining misses are substantive. Click's class header and `@property` decorator +lie outside callable spans. Redux's enhancer and complete store/observable API +are not fully represented by member excerpts. Six returned Redux callables lack +recorded source digests in both arms; native output correctly marks those +excerpts unverified. The external audit checks current pinned source without +upgrading the native provenance claim. WalkDir still lacks complete symlink-loop +and contents-first witnesses. Moving `check_loop` earlier does not also retrieve +all surrounding evidence required by the loop fact. + +**This is a Compass before/after experiment.** Graphify has no equivalent native +member-source/focus flag in the recorded interface. The separately registered +paired public-neighbor control remains Compass 14/20 versus Graphify 15/20 at +8,000 bytes. Its numbers must not be equated with or replaced by this native arm. +A common focused workflow must be evaluated on both tools before claiming a new +paired improvement. These are source-evidence results, not authored answers. + +Validation passes formatting, workspace Clippy, 19 focused/source integration +tests, all 42 CLI code-query tests, 1,106 workspace library/binary tests (two +existing ignores), nine CLI product tests, the product boundary, the CLI build, +and all 187 benchmark tests. Native tests cover late-member retrieval with the +same quota, duplicate terms, camel/snake case, Unicode, ties and shuffled input, +zero-match/default behavior, stale sources, ambiguous roots, invalid bounds and +CLI output formats. The initial compile failure from a wrong runtime-node +accessor is retained in round 01. The final source hashes match the production +commit and frozen evaluated binary. Existing unused-mut test warnings remain +visible. No JavaScript, browser, platform, packaging or extraction-fixture gate +was rerun because those production surfaces are unchanged. + +External `member-focus-02` retains all 15 CLI calls, source/binary hashes, +independent ordering/span/digest-status verification and identical repeated +replay. Its first verifier incorrectly assumed every source had a digest; that +failure is preserved, and the corrected verifier explicitly validates the six +unverified Redux excerpts. `explanation_focus_review.json` retains every fact, +member order, limit/provenance status, cost and artifact hash. Package version +remains 0.3.30; no graph schema, extraction, cache or history changes occur. +Authored answers, fair common-workflow focus comparison, longer walks, god-object +judgments, functional communities and held-out confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources. From 67e5550bf8c9eb3479e17bcc49524e92424795cb Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:45:29 -0700 Subject: [PATCH 95/97] audit: register paired member focus retrieval policy --- .../paired_member_focus_registration.json | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 benchmarks/agent_query/paired_member_focus_registration.json diff --git a/benchmarks/agent_query/paired_member_focus_registration.json b/benchmarks/agent_query/paired_member_focus_registration.json new file mode 100644 index 000000000..112f9e456 --- /dev/null +++ b/benchmarks/agent_query/paired_member_focus_registration.json @@ -0,0 +1,71 @@ +{ + "schema": "compass.paired-member-focus-registration/1", + "baselineCommit": "9f17e0ccd79f373d3201ec57ad930719c8370be9", + "scope": "Known five-language development subjects and previously inspected public labels. Fair common retrieval-policy experiment, not held-out evidence or authored answers.", + "sourceQuestions": "benchmarks/agent_query/responsibility_questions_panel_a.json", + "sourceQuestionsSha256": "608314dcd5e0a9e40fc7c6b8dd9d44026ba6a9b001e881fa8504fe44cc1fc7b2", + "tasks": [ + { + "repository": "chi", + "symbol": "Mux", + "file": "mux.go", + "startLine": 21, + "kind": "struct" + }, + { + "repository": "click", + "symbol": "_AtomicFile", + "file": "src/click/_compat.py", + "startLine": 455, + "kind": "class" + }, + { + "repository": "jsoup", + "symbol": "Cleaner", + "file": "src/main/java/org/jsoup/safety/Cleaner.java", + "startLine": 43, + "kind": "class" + }, + { + "repository": "redux", + "symbol": "createStore", + "file": "src/createStore.ts", + "startLine": 86, + "kind": "function" + }, + { + "repository": "walkdir", + "symbol": "IntoIter", + "file": "src/lib.rs", + "startLine": 566, + "kind": "struct" + } + ], + "graphRun": "java-state-access-02/run.json", + "graphRunSha256": "c315bffe56cbb7a7684c088e0219fc85ba9ef0681a39463608845691522d21ff", + "compassBinary": "member-focus-02/compass", + "compassBinarySha256": "e33934b94d1e254ba59906fddece098d8433bf7e31af746e46aac3dfc6744f97", + "priorCapture": "explanation-budget-01/capture/capture.json", + "priorCaptureSha256": "c2db3be974793b100dbb183eab35d474b0ed4d6d3c62f85aba6934c326738e0d", + "sourceBudgets": [ + 2000, + 4000, + 8000, + 16000, + 32000 + ], + "primaryBudget": 8000, + "identityControl": "Both tools receive the same known symbol, repository-relative file, declaration line and expected kind. Compass uses search_symbols exact=true, source_file, start_line, kind with existing limits (256 candidates, 500 nodes, 524288 response bytes). Graphify uses its existing file::symbol get_node; validate returned source identity and source-declaration kind afterward. Do not imply Graphify natively accepts all filters. One call per tool/subject, no retries. Preserve all ambiguity/error/limit outcomes.", + "capture": "Fresh identical public exact resolver and get_neighbors calls for each tool/subject; Compass uses the latest frozen binary, Graphify the same verified installation and graphs. Preserve raw calls, payload costs, source/graph/binary hashes and all errors. Compare every response to the previous capture.", + "selection": "Use only the returned outgoing contains/method label, file and start line. Retain every kind of member exposed by this common interface, including fields and nested containers; do not use Compass-only kind or end-byte metadata. Group equal file/line anchors. Normalize the full unchanged question with the frozen existing Compass query-term policy, identically for both tools. Normalize each returned label with the same identifier/code-token policy, including whole snake-case tokens and components. A group score is the maximum number of distinct matched question terms in any one of its labels; duplicate/alternative labels do not accumulate evidence. Sort by descending score, then original file/line order. Preserve unmatched groups and all ambiguities; no oracle/source-body ranking, language-specific adjustment or extra calls.", + "windows": "Determine source end boundaries BEFORE reranking: next greater returned anchor in the same file, or min(EOF,start+4096) for the final anchor. Rerank these fixed intervals and spend each registered shared raw-byte quota in that order. Never let reordered traversal extend an interval, read an oracle witness to select it, or duplicate a source interval. The unchanged source-order control uses the same data and all five quotas.", + "scoring": "Preserve all 20 facts at every budget. Report literal witness coverage and the prior fixed semantic allowance for click-1 (only class header missing, source identity verified and all other witness lines present). No partial-fact credit. Missing resolver, source, truncation or tool error remains explicit and cannot become a successful empty inventory. Raw source is evidence, never an authored answer.", + "verification": "Commit policy and tests before real score capture. Independently verify every raw call, source pin/hash, group/label score, ordered byte interval and fact result. Replay historical source-order scores exactly; expose response changes. Retain all per-repository/per-budget wins, ties, losses and actual source/payload costs. Distinguish fresh capture, offline interval planning and authored answer synthesis.", + "limitations": [ + "Previously inspected development panel; choices can be informed by prior misses.", + "A common helper is not a new native capability of Graphify or proof of equal compute.", + "Public member sets and labels differ by extraction. More fields can help or consume source quota; no tool-specific filtering is allowed.", + "Keep the native Compass member-focus 14/20 to 15/20 result separate: native callable spans differ from common public-anchor windows.", + "No conclusion can establish god-object defects, authored explanations, semantic completeness, broad path/community quality or overall superiority." + ] +} From 6c9876ee462e9f3a465b0f81c5350f6705fa3481 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:49:03 -0700 Subject: [PATCH 96/97] audit: freeze common member focus ranking and interval policy --- benchmarks/agent_query/member_focus.py | 125 ++++++++++ .../agent_query/member_focus_lexicon.json | 236 ++++++++++++++++++ benchmarks/agent_query/source_windows.py | 30 ++- .../agent_query/tests/test_member_focus.py | 85 +++++++ .../agent_query/tests/test_source_windows.py | 29 +++ 5 files changed, 499 insertions(+), 6 deletions(-) create mode 100644 benchmarks/agent_query/member_focus.py create mode 100644 benchmarks/agent_query/member_focus_lexicon.json create mode 100644 benchmarks/agent_query/tests/test_member_focus.py diff --git a/benchmarks/agent_query/member_focus.py b/benchmarks/agent_query/member_focus.py new file mode 100644 index 000000000..970782e76 --- /dev/null +++ b/benchmarks/agent_query/member_focus.py @@ -0,0 +1,125 @@ +"""Frozen common name-focus policy for paired public-member evaluation. + +Both tools use these same functions. No source body, graph file, witness, +language-specific synonym, or tool-specific metadata is consulted to rank. +""" +from collections import defaultdict +import json +from pathlib import Path +import re +import unicodedata + +from benchmarks.agent_query.community_tasks import read_bounded +from benchmarks.agent_query.source_windows import MAX_ROWS + +_LEXICON = json.loads(read_bounded(Path(__file__).with_name('member_focus_lexicon.json'), 65536)) +if _LEXICON['schema'] != 'compass.member-focus-lexicon/1': + raise ValueError('unsupported member focus lexicon') +_STOP = frozenset(_LEXICON['stopwords']) +_CANONICAL = _LEXICON['canonicalTokens'] + + +def identifier_tokens(text): + text = ''.join(c for c in unicodedata.normalize('NFKD', text) + if unicodedata.category(c) not in {'Mn', 'Mc', 'Me'}) + out = [] + for i, character in enumerate(text): + previous = text[i - 1] if i else '' + following = text[i + 1] if i + 1 < len(text) else '' + if character.isupper() and previous and ( + previous.islower() or previous.isnumeric() + or (previous.isupper() and following.islower())): + out.append(' ') + out.append(character) + return re.findall(r'\w+', ''.join(out).lower()) + + +def _ending(stem): + if len(stem) > 1 and stem[-1] == stem[-2]: + stem = stem[:-1] + return stem + 'e' if stem.endswith(('at', 'abl', 'il', 'v')) else stem + + +def canonical(token): + if not token.isascii(): + return token + if token in _CANONICAL: + return _CANONICAL[token] + if token.endswith('ies') and len(token[:-3]) >= 2: + return token[:-3] + 'y' + if token.endswith(('sses', 'xes', 'zes', 'ches', 'shes', 'uses')): + return token[:-2] + if (token.endswith('s') and len(token[:-1]) >= 3 + and not token[:-1].endswith(('s', 'u', 'i', 'a'))): + return token[:-1] + if token.endswith('ing') and len(token[:-3]) >= 3: + return _ending(token[:-3]) + if token.endswith('ied') and len(token[:-3]) >= 2: + return token[:-3] + 'y' + if token.endswith('ed') and len(token[:-2]) >= 3: + return _ending(token[:-2]) + return token + + +def _searchable(token): + return not all('a' <= c <= 'z' for c in token) or len(token) > 2 + + +def focus_terms(question): + if not isinstance(question, str) or len(question.encode()) > 4096: + raise ValueError('focus must be text within 4096 bytes') + raw = [] + for word in question.split(): + if any('\u4e00' <= c <= '\u9fff' for c in word): + lowered = word.lower() + if len(lowered) < 2: + if _searchable(lowered): + raw.append(lowered) + else: + raw.extend(lowered[i:i + 2] for i in range(len(lowered) - 1) + if _searchable(lowered[i:i + 2])) + if _searchable(lowered) and lowered not in raw: + raw.append(lowered) + else: + raw.extend(t for t in identifier_tokens(word) if _searchable(t)) + content = [term for term in raw if term not in _STOP] + result = sorted({term if term in _STOP else canonical(term) for term in content or raw}) + if not 1 <= len(result) <= 32: + raise ValueError('focus requires 1 to 32 distinct searchable terms') + return result + + +def name_terms(name): + result = set() + for token in identifier_tokens(name): + result.add(canonical(token)) + if '_' in token: + result.update(canonical(part) for part in token.split('_') if part) + return result + + +def focus_groups(rows, question): + terms = focus_terms(question) + if not isinstance(rows, list) or len(rows) > MAX_ROWS: + raise ValueError('membership row limit exceeded') + groups = defaultdict(set) + total = 0 + for row in rows: + if not isinstance(row, dict): + raise ValueError('invalid membership row') + label, file, line = row.get('label'), row.get('file'), row.get('line') + if (not isinstance(label, str) or not label or len(label.encode()) > 4096 + or not isinstance(file, str) or not file or type(line) is not int or line < 1): + raise ValueError('invalid membership label or source anchor') + total += len(label.encode()) + len(file.encode()) + if total > 1048576: + raise ValueError('membership label metadata limit exceeded') + groups[file, line].add(label) + ranked = [] + for (file, line), labels in sorted(groups.items()): + matches = [dict(label=label, matchedTerms=sorted(name_terms(label) & set(terms))) + for label in sorted(labels)] + ranked.append(dict(file=file, line=line, score=max(len(m['matchedTerms']) for m in matches), + labels=matches)) + ranked.sort(key=lambda group: (-group['score'], group['file'], group['line'])) + return dict(focusTerms=terms, groups=ranked) diff --git a/benchmarks/agent_query/member_focus_lexicon.json b/benchmarks/agent_query/member_focus_lexicon.json new file mode 100644 index 000000000..253c39afe --- /dev/null +++ b/benchmarks/agent_query/member_focus_lexicon.json @@ -0,0 +1,236 @@ +{ + "schema": "compass.member-focus-lexicon/1", + "sourceCommit": "293582c38085a195b9a8272c8828196bc3724aaf", + "sourceSha256": { + "crates/compass-model/src/lexical.rs": "145f751ba0755c29adbb6498d206fbf729aa62a9559ad58d256a05b3e43f50ee", + "crates/compass-query/src/text.rs": "4df8680d3af04cc8be70c6f4279fda6a928b83c1a732d3d0f43132c7cf05e177" + }, + "stopwords": [ + "about", + "aendert", + "and", + "any", + "are", + "aus", + "avec", + "be", + "been", + "bei", + "being", + "but", + "can", + "cette", + "che", + "como", + "cosa", + "could", + "cu\u00e1l", + "cu\u00e1les", + "cu\u00e1ndo", + "c\u00f3mo", + "dans", + "das", + "della", + "dem", + "den", + "der", + "did", + "die", + "does", + "donde", + "dove", + "d\u00f3nde", + "ein", + "eine", + "es", + "est", + "est\u00e1", + "est\u00e1n", + "est\u00e3o", + "fonctionne", + "for", + "from", + "fuer", + "funciona", + "funktioniert", + "funziona", + "f\u00fcr", + "geaendert", + "ge\u00e4ndert", + "gibt", + "haben", + "had", + "has", + "hat", + "have", + "hay", + "here", + "how", + "implement", + "implementation", + "implemented", + "into", + "is", + "ist", + "its", + "kann", + "koennen", + "k\u00f6nnen", + "may", + "might", + "mit", + "muss", + "must", + "nach", + "nicht", + "not", + "n\u00e3o", + "oder", + "off", + "onde", + "onto", + "o\u00f9", + "para", + "perch\u00e9", + "por", + "porque", + "pourquoi", + "quais", + "qual", + "quale", + "quali", + "quand", + "quando", + "que", + "quel", + "quelle", + "quelles", + "quels", + "qui", + "quoi", + "qu\u00e9", + "shall", + "should", + "sich", + "sind", + "soll", + "some", + "sono", + "sont", + "s\u00e3o", + "tem", + "that", + "the", + "their", + "them", + "there", + "these", + "they", + "this", + "those", + "ueber", + "uma", + "und", + "von", + "wann", + "warum", + "was", + "welche", + "welcher", + "welches", + "wer", + "were", + "what", + "when", + "where", + "which", + "who", + "whom", + "whose", + "wie", + "wieso", + "will", + "wird", + "with", + "without", + "wo", + "work", + "working", + "works", + "would", + "wurde", + "\u00e4ndert", + "\u00fcber" + ], + "canonicalTokens": { + "added": "add", + "adding": "add", + "aliases": "alias", + "changed": "change", + "changing": "change", + "compacted": "compact", + "compacting": "compact", + "compiled": "compile", + "compiling": "compile", + "configured": "configure", + "configuring": "configure", + "converted": "convert", + "converting": "convert", + "created": "create", + "creating": "create", + "deleted": "delete", + "deleting": "delete", + "dispatched": "dispatch", + "dispatching": "dispatch", + "enabled": "enable", + "enabling": "enable", + "executed": "execute", + "executing": "execute", + "formatted": "format", + "formatting": "format", + "handled": "handle", + "handling": "handle", + "implemented": "implement", + "implementing": "implement", + "invoked": "invoke", + "invoking": "invoke", + "loaded": "load", + "loading": "load", + "mapped": "map", + "mapping": "map", + "merged": "merge", + "merging": "merge", + "opened": "open", + "opening": "open", + "optimized": "optimize", + "optimizing": "optimize", + "parsed": "parse", + "parsing": "parse", + "processed": "process", + "processing": "process", + "recognized": "recognize", + "recognizing": "recognize", + "registered": "register", + "registering": "register", + "represented": "represent", + "representing": "represent", + "resolution": "resolve", + "resolved": "resolve", + "resolver": "resolve", + "resolving": "resolve", + "restored": "restore", + "restoring": "restore", + "routing": "route", + "scheduled": "schedule", + "scheduling": "schedule", + "searched": "search", + "searching": "search", + "sending": "send", + "sent": "send", + "solved": "solve", + "solving": "solve", + "tracked": "track", + "tracking": "track", + "using": "use" + } +} diff --git a/benchmarks/agent_query/source_windows.py b/benchmarks/agent_query/source_windows.py index c6b7104da..17e7ed099 100644 --- a/benchmarks/agent_query/source_windows.py +++ b/benchmarks/agent_query/source_windows.py @@ -19,7 +19,7 @@ def sha(data): return hashlib.sha256(data).hexdigest() -def source_windows(root, rows, budget): +def source_windows(root, rows, budget, *, ordered_groups=None): if type(budget) is not int or not 1 <= budget <= 1048576: raise ValueError('source budget must be an integer from 1 to 1048576') if not isinstance(rows, list) or len(rows) > MAX_ROWS: @@ -57,16 +57,34 @@ def source_windows(root, rows, budget): if line >= len(offsets[file]): raise ValueError('membership line outside source') keys = sorted(groups) + if ordered_groups is None: + visit = keys + else: + if not isinstance(ordered_groups, list) or len(ordered_groups) != len(keys): + raise ValueError('window order must contain every source group exactly once') + for key in ordered_groups: + if (not isinstance(key, (list, tuple)) or len(key) != 2 + or not isinstance(key[0], str) or type(key[1]) is not int): + raise ValueError('invalid ordered source group') + visit = [tuple(key) for key in ordered_groups] + if len(set(visit)) != len(keys) or set(visit) != set(keys): + raise ValueError('window order must contain every source group exactly once') + # End boundaries belong to source order, never to the chosen visit order. + ends = {} + for index, (file, line) in enumerate(keys): + following = keys[index + 1] if index + 1 < len(keys) else None + start = offsets[file][line - 1] + ends[file, line] = (offsets[file][following[1] - 1] + if following and following[0] == file + else min(len(files[file]), start + 4096)) windows = [] remaining = budget - for index, (file, line) in enumerate(keys): + for file, line in visit: if not remaining: break data = files[file] start = offsets[file][line - 1] - following = keys[index + 1] if index + 1 < len(keys) else None - end = (offsets[file][following[1] - 1] if following and following[0] == file - else min(len(data), start + 4096)) + end = ends[file, line] kept_end = min(end, start + remaining) part = data[start:kept_end] windows.append(dict(file=file, startLine=line, startByte=start, endByte=kept_end, @@ -78,7 +96,7 @@ def source_windows(root, rows, budget): if read_bounded(root / file, MAX_FILE_BYTES) != data: raise ValueError('source changed during window planning') return dict(budget=budget, sourceBytes=budget - remaining, windows=windows, - omittedGroups=[dict(file=f, line=n) for f, n in keys[len(windows):]], + omittedGroups=[dict(file=f, line=n) for f, n in visit[len(windows):]], sourceReadBytes=total, missingAnchorRows=[]) diff --git a/benchmarks/agent_query/tests/test_member_focus.py b/benchmarks/agent_query/tests/test_member_focus.py new file mode 100644 index 000000000..a9690b8ee --- /dev/null +++ b/benchmarks/agent_query/tests/test_member_focus.py @@ -0,0 +1,85 @@ +import hashlib +import json +from pathlib import Path +import unittest + +from benchmarks.agent_query.member_focus import canonical, focus_groups, focus_terms, name_terms + + +class MemberFocusTests(unittest.TestCase): + def row(self, label, line=1, file='x'): + return dict(label=label, file=file, line=line) + + def test_frozen_lexicon_matches_recorded_production_sources(self): + lexicon = json.loads(Path('benchmarks/agent_query/member_focus_lexicon.json').read_text()) + for file, expected in lexicon['sourceSha256'].items(): + self.assertEqual(hashlib.sha256(Path(file).read_bytes()).hexdigest(), expected) + + def test_terms_match_the_five_captured_native_focus_questions(self): + questions = json.loads(Path('benchmarks/agent_query/responsibility_questions_panel_a.json').read_text()) + prior = json.loads(Path('benchmarks/agent_query/explanation_focus_review.json').read_text()) + for case in questions['cases']: + expected = next(r['focusTerms'] for r in prior['results'] + if r['repository'] == case['repository'] and r['arm'] == 'focused') + self.assertEqual(focus_terms(case['question']), expected) + + def test_case_snake_camel_morphology_and_unicode(self): + terms = set(focus_terms('checking loops')) + for name in ['checkLoop', '.check_loop()', 'CHECK_LOOP']: + self.assertEqual(name_terms(name) & terms, {'check', 'loop'}) + self.assertEqual(name_terms('Résumé'), {'resume'}) + self.assertEqual(focus_terms('résumé'), ['resume']) + self.assertEqual(focus_terms('路由处理'), ['处理', '由处', '路由', '路由处理']) + for raw, expected in [('routing', 'route'), ('dependencies', 'dependency'), + ('mapped', 'map'), ('using', 'use')]: + self.assertEqual(canonical(raw), expected) + + def test_duplicate_query_terms_do_not_add_weight(self): + self.assertEqual(focus_terms('loop loop loops'), ['loop']) + self.assertEqual(focus_groups([self.row('loopLoop')], 'loop')['groups'][0]['score'], 1) + + def test_duplicate_and_alternate_labels_use_maximum_not_union(self): + rows = [self.row('check'), self.row('loop'), self.row('check')] + report = focus_groups(rows, 'check loop') + self.assertEqual(len(report['groups']), 1) + self.assertEqual(report['groups'][0]['score'], 1) + self.assertEqual([r['label'] for r in report['groups'][0]['labels']], ['check', 'loop']) + + def test_highest_score_first_then_source_order_independent_of_rows(self): + rows = [self.row('loop', 3), self.row('loop', 1), self.row('checkLoop', 2)] + expected = focus_groups(rows, 'check loop') + self.assertEqual([g['line'] for g in expected['groups']], [2, 1, 3]) + self.assertEqual(focus_groups(list(reversed(rows)), 'check loop'), expected) + + def test_zero_matches_preserve_all_groups_and_source_order(self): + rows = [self.row('first', 10), self.row('second', 1), self.row('third', 1, 'a')] + report = focus_groups(rows, 'absent') + self.assertEqual([(g['file'], g['line']) for g in report['groups']], [('a', 1), ('x', 1), ('x', 10)]) + self.assertTrue(all(g['score'] == 0 for g in report['groups'])) + + def test_paths_kinds_and_other_tool_metadata_do_not_rank(self): + row = self.row('plain', file='loop/check.rs') + row.update(kind='loop', sourceText='loop', id='loop', score=999) + self.assertEqual(focus_groups([row], 'loop')['groups'][0]['score'], 0) + + def test_empty_invalid_and_excessive_queries_fail(self): + for query in ['', '!!!', None, 'x' * 4097, ' '.join(f'term{i}' for i in range(33))]: + with self.subTest(query=query), self.assertRaises(ValueError): + focus_terms(query) + self.assertEqual(focus_terms('With'), ['with']) + + def test_invalid_labels_and_anchors_fail(self): + for row in [None, {}, self.row('', 1), self.row('loop', True), + self.row('loop', 0), self.row('loop', file=''), self.row('x' * 4097)]: + with self.subTest(row=row), self.assertRaises(ValueError): + focus_groups([row], 'loop') + + def test_row_and_aggregate_metadata_bounds(self): + with self.assertRaises(ValueError): + focus_groups([self.row('loop')] * 10001, 'loop') + with self.assertRaises(ValueError): + focus_groups([self.row('loop', file='x' * 2000)] * 1000, 'loop') + + +if __name__ == '__main__': + unittest.main() diff --git a/benchmarks/agent_query/tests/test_source_windows.py b/benchmarks/agent_query/tests/test_source_windows.py index c7d81ee48..6b8303ef1 100644 --- a/benchmarks/agent_query/tests/test_source_windows.py +++ b/benchmarks/agent_query/tests/test_source_windows.py @@ -33,6 +33,35 @@ def test_quota_exhaustion_retains_omissions(self): self.assertEqual(r['omittedGroups'], [dict(file='x', line=3)]) self.assertFalse(score_windows(self.root, self.case(), r)[0]['sufficientSourceEvidence']) + def test_reranking_keeps_original_interval_ends(self): + rows = [dict(file='x', line=i) for i in [1, 2, 3, 4]] + order = [('x', 3), ('x', 1), ('x', 4), ('x', 2)] + control = source_windows(self.root, rows, 8, ordered_groups=order) + self.assertEqual([(w['startByte'], w['endByte'], w['requestedEndByte']) + for w in control['windows']], [(13, 19, 19), (0, 2, 6)]) + self.assertEqual(control['omittedGroups'], [dict(file='x', line=4), dict(file='x', line=2)]) + self.assertTrue(score_windows(self.root, self.case('third', 3, 3), control)[0]['sufficientSourceEvidence']) + + def test_reranking_retains_last_anchor_cap_and_other_file_boundaries(self): + (self.root / 'y').write_bytes(b'y' * 5000) + rows = self.rows + [dict(file='y', line=1)] + control = source_windows(self.root, rows, 32000, + ordered_groups=[('y', 1), ('x', 3), ('x', 1)]) + self.assertEqual(control['windows'][0]['endByte'], 4096) + self.assertEqual(control['windows'][1]['endByte'], len(self.data)) + self.assertEqual(control['windows'][2]['endByte'], 13) + + def test_reordered_groups_must_be_an_exact_permutation(self): + for order in [[], [('x', 1), ('x', 1)], [('x', 1), ('other', 3)], + [('x', True), ('x', 3)], [('x', 1), ['x']], 'bad']: + with self.subTest(order=order), self.assertRaises(ValueError): + source_windows(self.root, self.rows, 10, ordered_groups=order) + + def test_explicit_source_order_is_identical_to_default(self): + self.assertEqual(source_windows(self.root, self.rows, 16), + source_windows(self.root, self.rows, 16, + ordered_groups=[('x', 1), ('x', 3)])) + def test_last_window_has_4096_byte_cap(self): (self.root / 'x').write_bytes(b'a' * 5000) r = source_windows(self.root, [self.rows[0]], 32000) From ce8b739417442a9d2a3ce8da0c2913d76e85f4e6 Mon Sep 17 00:00:00 2001 From: forhappy Date: Sun, 27 Sep 2026 07:58:21 -0700 Subject: [PATCH 97/97] audit: retain paired focus regressions and source-window causes --- benchmarks/agent_query/README.md | 23 + .../paired_member_focus_review.json | 3093 +++++++++++++++++ ...ode-graph-intelligence-audit-2026-09-26.md | 85 + 3 files changed, 3201 insertions(+) create mode 100644 benchmarks/agent_query/paired_member_focus_review.json diff --git a/benchmarks/agent_query/README.md b/benchmarks/agent_query/README.md index ba2abf536..9cb87b656 100644 --- a/benchmarks/agent_query/README.md +++ b/benchmarks/agent_query/README.md @@ -707,3 +707,26 @@ Redux digests. Native and benchmark checks are listed in the main audit. This is not a paired Graphify result or authored-answer score. The separate symmetric 8,000-byte neighbor-window control remains 14/20 versus 15/20. Remaining WalkDir loop evidence shows why lexical name matching alone is insufficient. + +### Paired public-member focus: retain the negative result + +`paired_member_focus_registration.json` fixes a common helper and five source +quotas for both tools before capture. `member_focus.py` ranks public labels with +a frozen lexical policy; equal file/line groups use the maximum single-label +score, and ties keep source order. `source_windows.py` accepts an exact group +permutation while preserving interval ends computed from original source order. +No tool-specific kind filter, source-body ranking or private graph lookup selects +source. The helper is an evaluation policy, not a new native Graphify feature. + +At the primary 8,000-byte quota, common focus regresses **Compass 14/20 → 12/20** +and **Graphify 15/20 → 14/20**. Both trade Chi shared-state evidence for routing +evidence; Compass loses two Redux facts and Graphify loses one WalkDir fact. +The committed `paired_member_focus_review.json` publishes all five quotas and +all fact-level gains/losses. This policy is rejected as the default. Keep its +negative result separate from the native callable-span improvement. + +All 202 benchmark tests pass, including 15 new tests. All 20 fresh public calls, +153 anchors and 100 window/scoring arms replay independently; all historical +source-order controls remain identical. External `paired-member-focus-01` +retains raw responses, windows, ranked labels, source-boundary diagnosis and +verification scripts. Product code/version is unchanged in this checkpoint. diff --git a/benchmarks/agent_query/paired_member_focus_review.json b/benchmarks/agent_query/paired_member_focus_review.json new file mode 100644 index 000000000..b3f676b43 --- /dev/null +++ b/benchmarks/agent_query/paired_member_focus_review.json @@ -0,0 +1,3093 @@ +{ + "schema": "compass.paired-member-focus-review/1", + "registration": "benchmarks/agent_query/paired_member_focus_registration.json", + "registrationSha256": "0d8fbb14d6f2f8f6adbaf420aabe2403bbedd2def5ab80708138d82269697d51", + "registrationCommit": "67e5550b", + "policyCommit": "6c9876ee", + "productCommit": "293582c38085a195b9a8272c8828196bc3724aaf", + "evaluatedBinarySha256": "e33934b94d1e254ba59906fddece098d8433bf7e31af746e46aac3dfc6744f97", + "scope": "Known five-language development subjects and previously inspected public labels. Fair common retrieval-policy experiment, not held-out evidence or authored answers.", + "summary": [ + { + "mode": "source-order", + "budget": 2000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 10000 + }, + { + "mode": "source-order", + "budget": 2000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 10000 + }, + { + "mode": "source-order", + "budget": 4000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 19673 + }, + { + "mode": "source-order", + "budget": 4000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 19673 + }, + { + "mode": "source-order", + "budget": 8000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 14, + "literalWitnessFacts": 13, + "sourceBytes": 35673 + }, + { + "mode": "source-order", + "budget": 8000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 15, + "literalWitnessFacts": 14, + "sourceBytes": 35673 + }, + { + "mode": "source-order", + "budget": 16000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 19, + "literalWitnessFacts": 18, + "sourceBytes": 59542 + }, + { + "mode": "source-order", + "budget": 16000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53417 + }, + { + "mode": "source-order", + "budget": 32000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 20, + "literalWitnessFacts": 19, + "sourceBytes": 64644 + }, + { + "mode": "source-order", + "budget": 32000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53747 + }, + { + "mode": "name-focus", + "budget": 2000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 6, + "literalWitnessFacts": 5, + "sourceBytes": 10000 + }, + { + "mode": "name-focus", + "budget": 2000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 8, + "literalWitnessFacts": 7, + "sourceBytes": 10000 + }, + { + "mode": "name-focus", + "budget": 4000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 9, + "literalWitnessFacts": 8, + "sourceBytes": 19673 + }, + { + "mode": "name-focus", + "budget": 4000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 10, + "literalWitnessFacts": 9, + "sourceBytes": 19673 + }, + { + "mode": "name-focus", + "budget": 8000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 12, + "literalWitnessFacts": 11, + "sourceBytes": 35673 + }, + { + "mode": "name-focus", + "budget": 8000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 14, + "literalWitnessFacts": 13, + "sourceBytes": 35673 + }, + { + "mode": "name-focus", + "budget": 16000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 19, + "literalWitnessFacts": 18, + "sourceBytes": 59542 + }, + { + "mode": "name-focus", + "budget": 16000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53417 + }, + { + "mode": "name-focus", + "budget": 32000, + "tool": "compass", + "facts": 20, + "sourceEvidenceFacts": 20, + "literalWitnessFacts": 19, + "sourceBytes": 64644 + }, + { + "mode": "name-focus", + "budget": 32000, + "tool": "graphify", + "facts": 20, + "sourceEvidenceFacts": 18, + "literalWitnessFacts": 17, + "sourceBytes": 53747 + } + ], + "results": [ + { + "repository": "chi", + "tool": "compass", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-2", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "chi-1", + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-1" + ] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "compass", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-3", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "chi-1", + "chi-2", + "chi-4" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "chi-1", + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "chi-1" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 15583, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 3673, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 3, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "jsoup-3" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "jsoup-1", + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "jsoup-3" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 9724, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "jsoup-3", + "jsoup-4" + ] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 9544, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-1", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-1", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 14562, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "redux-1", + "redux-2", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-1", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "redux-1", + "redux-3", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "redux", + "tool": "graphify", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 8617, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "redux-1", + "redux-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "walkdir-3" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 21102, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 3, + "literalWitnessFacts": 3, + "missingFacts": [ + "walkdir-3" + ] + }, + { + "repository": "walkdir", + "tool": "compass", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 21102, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "source-order", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "source-order", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "source-order", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 2, + "literalWitnessFacts": 2, + "missingFacts": [ + "walkdir-2", + "walkdir-3" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "source-order", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "source-order", + "budget": 32000, + "sourceBytes": 16330, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "name-focus", + "budget": 2000, + "sourceBytes": 2000, + "sourceEvidenceFacts": 0, + "literalWitnessFacts": 0, + "missingFacts": [ + "walkdir-1", + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "name-focus", + "budget": 4000, + "sourceBytes": 4000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "name-focus", + "budget": 8000, + "sourceBytes": 8000, + "sourceEvidenceFacts": 1, + "literalWitnessFacts": 1, + "missingFacts": [ + "walkdir-2", + "walkdir-3", + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "name-focus", + "budget": 16000, + "sourceBytes": 16000, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "mode": "name-focus", + "budget": 32000, + "sourceBytes": 16330, + "sourceEvidenceFacts": 4, + "literalWitnessFacts": 4, + "missingFacts": [] + } + ], + "factDeltas": [ + { + "repository": "chi", + "tool": "compass", + "budget": 2000, + "gainedFacts": [ + "chi-3" + ], + "lostFacts": [ + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 4000, + "gainedFacts": [ + "chi-3", + "chi-4" + ], + "lostFacts": [ + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 8000, + "gainedFacts": [ + "chi-4" + ], + "lostFacts": [ + "chi-1" + ] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "chi", + "tool": "compass", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 2000, + "gainedFacts": [ + "chi-3" + ], + "lostFacts": [ + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 4000, + "gainedFacts": [ + "chi-3", + "chi-4" + ], + "lostFacts": [ + "chi-2" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 8000, + "gainedFacts": [ + "chi-4" + ], + "lostFacts": [ + "chi-1" + ] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "chi", + "tool": "graphify", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "compass", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "click", + "tool": "graphify", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [ + "jsoup-1" + ] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "compass", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "jsoup", + "tool": "graphify", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [ + "redux-1" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 4000, + "gainedFacts": [ + "redux-2" + ], + "lostFacts": [ + "redux-1" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [ + "redux-1", + "redux-3" + ] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "compass", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 2000, + "gainedFacts": [ + "redux-2" + ], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 4000, + "gainedFacts": [ + "redux-2" + ], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "redux", + "tool": "graphify", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "compass", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 2000, + "gainedFacts": [], + "lostFacts": [ + "walkdir-1" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 4000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 8000, + "gainedFacts": [], + "lostFacts": [ + "walkdir-4" + ] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 16000, + "gainedFacts": [], + "lostFacts": [] + }, + { + "repository": "walkdir", + "tool": "graphify", + "budget": 32000, + "gainedFacts": [], + "lostFacts": [] + } + ], + "publicCallCosts": { + "compass": { + "resolver": { + "textBytes": 3039, + "wireResponseBytes": 21983 + }, + "neighbors": { + "textBytes": 33061, + "wireResponseBytes": 372871 + } + }, + "graphify": { + "resolver": { + "textBytes": 619, + "wireResponseBytes": 1094 + }, + "neighbors": { + "textBytes": 6980, + "wireResponseBytes": 7534 + } + } + }, + "publicCalls": 20, + "windowArms": 100, + "historicalComparisons": [ + { + "repository": "chi", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "chi", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "click", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "click", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "jsoup", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "jsoup", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "redux", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "redux", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "walkdir", + "tool": "compass", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + }, + { + "repository": "walkdir", + "tool": "graphify", + "resolverResponseUnchanged": true, + "neighborResponseUnchanged": true, + "membershipRowsUnchanged": true + } + ], + "diagnosis": { + "repository": "redux", + "tool": "compass", + "mode": "name-focus", + "budget": 8000, + "ownerId": "sha256:ceae4b39dd8902b6888096a1827ee67422d04d3c19d527d674ec740c3e903785", + "ownerSource": { + "file": "src/createStore.ts", + "startByte": 3002, + "endByte": 13827, + "startLine": 86, + "startColumn": 7, + "endLine": 395, + "endColumn": 1 + }, + "window": { + "file": "src/createStore.ts", + "startLine": 390, + "startByte": 13700, + "endByte": 17586, + "requestedEndByte": 17586, + "sourceBytes": 3886, + "sourceSha256": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "fileSha256": "4dc8195c8fb1cceb8bd182b1469eaf4978049a767f00b4558c1ddb9c1b398695" + }, + "lastReturnedLine": 489, + "bytesAfterOwner": 3759, + "scope": "Post-capture diagnosis using the graph owner span. This private graph metadata did not select, rank, clip or score any source window." + }, + "decision": "Reject common name-first public-anchor windows as the default explanation retrieval policy. The primary 8000-byte result regresses for both tools and Compass still trails Graphify. Retain optional native member focus and its separate evidence; do not extrapolate its native-span result to this workflow.", + "findings": [ + "At 8000 source bytes, source order scores Compass 14/20 versus Graphify 15/20; common name focus scores 12/20 versus 14/20. All results and losses are retained.", + "Both tools exchange chi-1 for chi-4: routing names prioritize routeHTTP but the With shared-state witness no longer fits. The fixed query normalization removes With as a stopword in the full question.", + "Compass loses Redux enhancer and listener-snapshot facts. A matching getState binding at line 390 retains a 3886-byte final window, of which 3759 bytes follow the createStore function boundary. This demonstrates a limitation of next-anchor/final-window selection, not of declared source spans.", + "Graphify loses the WalkDir contents-first fact at 8000 bytes after prioritizing check_loop. Neither tool gains the complete multi-method symlink-loop fact at that quota.", + "At 4000 bytes focus gains evidence for both tools, while larger quotas have unchanged totals. All five quotas are published; the primary result is not replaced by a favorable quota.", + "All 20 fresh resolver/neighbor responses and all public membership rows match the prior capture. All 50 source-order window/scoring arms reproduce the previous results exactly." + ], + "verification": { + "benchmarkTests": 202, + "newTests": 15, + "productBoundaryPassed": true, + "publicCallsVerified": 20, + "membershipAnchorsVerified": 153, + "windowArmsVerified": 100, + "replay": { + "exitCode": 0, + "byteIdentical": true, + "verifiedSummarySha256": "683ae9490003eb18c55c58142088418c36ef91fc8bb58bb2a447d9029d1cd7c5" + } + }, + "limitations": [ + "Previously inspected development panel; choices can be informed by prior misses.", + "A common helper is not a new native capability of Graphify or proof of equal compute.", + "Public member sets and labels differ by extraction. More fields can help or consume source quota; no tool-specific filtering is allowed.", + "Keep the native Compass member-focus 14/20 to 15/20 result separate: native callable spans differ from common public-anchor windows.", + "No conclusion can establish god-object defects, authored explanations, semantic completeness, broad path/community quality or overall superiority.", + "The independent verifier explicitly covers this ASCII question/label panel; the helper additionally has Unicode normalization tests, without a universal equivalence claim.", + "Literal witness scores remain separate and are one lower per tool at every quota because of the existing Click class-header allowance.", + "Equal source quotas do not imply equal payload, token, compute or actual retained-source costs. Compass graph responses remain much larger.", + "Source between anchors can include comments or subsequent declarations. No post-hoc owner-span clipping changes the registered outcome.", + "No Rust, JavaScript, platform or packaging checks were rerun: this turn changes evaluation code and evidence only." + ], + "artifacts": { + "paired-member-focus-01/benchmark-tests.log": "87228c8572711c416824d9b327a20865e21973700f85e92bca70b06c7b54aa2c", + "paired-member-focus-01/capture/capture.json": "27dbd87380ec059154e1b28d5b0837e5a4ad5069c5005ed90b7233c14128b926", + "paired-member-focus-01/capture/capture.py": "07d0f062dd38dc5469aea24c0eeeae921bbc9fce29be3d0134e75d54a3e42edc", + "paired-member-focus-01/capture/community_identity.py": "5bc3d1c574211aa3180a2334fe6c758043f384651948b7077aba3ea879de4503", + "paired-member-focus-01/capture/community_navigation.py": "808df1c7b15c8c2afc86ac1ed09dc4f339c98a77d7a7b14eb4866e3da1c8c0f8", + "paired-member-focus-01/capture/community_tasks.py": "77f599ff7ee7eebfe4b590fee6c771ecfb3ed475d1944e37bcabc110634d3660", + "paired-member-focus-01/capture/graphify-mcp-environment.json": "5c09a057c24e5d8528aa8e88d9195e1298b87fcc5d6b928e935bb439bc3c0535", + "paired-member-focus-01/capture/mcp_compare.py": "d23f093a1300fcf825457f917694097f0f8b8dc8ee8d671946032f5e4e89a5dc", + "paired-member-focus-01/capture/mcp_transport.py": "daaad69c554f824a1df94f0ef93cb9d4c6e9b4f4b712d1611323d524e10d44ee", + "paired-member-focus-01/capture/member_focus.py": "2e3208b84b22c6f23359d81d312f67b00778269f189dd2c1dbdb7dc46b7dfc28", + "paired-member-focus-01/capture/member_focus_lexicon.json": "908fad1e83a36bb19fde87a0197dbdca5e22e8b0af9c6d02c4b19225d02abc74", + "paired-member-focus-01/capture/paired_member_focus_registration.json": "0d8fbb14d6f2f8f6adbaf420aabe2403bbedd2def5ab80708138d82269697d51", + "paired-member-focus-01/capture/raw/chi/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/chi/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "paired-member-focus-01/capture/raw/chi/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/chi/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/chi/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "paired-member-focus-01/capture/raw/chi/compass/mcp/04.request.json": "34232dc4cae123d91883f2efff5f57b566033c899dbfaa93e98c0050c7f62a3c", + "paired-member-focus-01/capture/raw/chi/compass/mcp/04.response.jsonl": "7938a7e7afec02546e9bae682eefd8973e7dbccbd446fece89aecbfccdbbdb7d", + "paired-member-focus-01/capture/raw/chi/compass/mcp/05.request.json": "350ecbf25523d5f934821418e2b92aa51e54ac60547d2f2ba2787b5aee29c14e", + "paired-member-focus-01/capture/raw/chi/compass/mcp/05.response.jsonl": "09b3992280dc91e7c6a9203f46e1c8b994742d81bc4c2f179b3160c2adf3f08a", + "paired-member-focus-01/capture/raw/chi/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/019.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/020.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/021.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/022.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/023.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/024.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/025.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/026.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/027.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/028.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/029.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/030.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/031.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/16000/032.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/2000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/2000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/2000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/2000/003.source": "da6365e7a3222ee9638590c300eea3332ef3c6ec7437e8ab7548985678436047", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/019.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/020.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/021.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/022.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/023.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/024.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/025.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/026.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/027.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/028.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/029.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/030.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/031.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/32000/032.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/4000/007.source": "d18d2e71dded55d38c074b71d0ad73f9a9768f88eb4001b2e485fb67e442e735", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/name-focus/8000/019.source": "00c4285274fcc5d6fba2ee58daf0d8c2b9b825b68d35d65d0e90a9bb333a51b5", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/16000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/2000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/2000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/2000/002.source": "21c866f0d603e57248e2400aeaf96ff1167fac4310cac372c28986e727c32649", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/32000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/4000/010.source": "18c9e1ec72927bc57f70d271df8bd2a55e9b1dd6852595b02f63ce83430a4883", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/compass/windows/source-order/8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/04.request.json": "5212851af235eed2bd62a674fa14313ea9a38591a8d706897051badabc911735", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/04.response.jsonl": "d4d8b29632b3aae99cc57178a3b6ccb52935ae78e6848569b08c64f60b5a75a8", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/05.request.json": "43b918f02295bb3b8b86531c0b4f139acb79116d408969e2655c76a0601119e3", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/05.response.jsonl": "43678e134e41a496da5198713dbc822810d661c9644a5b50fda48dfa6e13b552", + "paired-member-focus-01/capture/raw/chi/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/019.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/020.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/021.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/022.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/023.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/024.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/025.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/026.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/027.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/028.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/029.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/030.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/031.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/16000/032.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/2000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/2000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/2000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/2000/003.source": "da6365e7a3222ee9638590c300eea3332ef3c6ec7437e8ab7548985678436047", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/019.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/020.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/021.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/022.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/023.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/024.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/025.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/026.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/027.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/028.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/029.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/030.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/031.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/32000/032.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/4000/007.source": "d18d2e71dded55d38c074b71d0ad73f9a9768f88eb4001b2e485fb67e442e735", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/000.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/001.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/002.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/003.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/004.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/005.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/006.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/007.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/008.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/009.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/010.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/011.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/012.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/013.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/014.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/015.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/016.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/017.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/018.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/name-focus/8000/019.source": "00c4285274fcc5d6fba2ee58daf0d8c2b9b825b68d35d65d0e90a9bb333a51b5", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/16000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/2000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/2000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/2000/002.source": "21c866f0d603e57248e2400aeaf96ff1167fac4310cac372c28986e727c32649", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/020.source": "4e1f899c6724bb85cac5ffda751aeb49d0861aaa81ecd93b50e614913a195955", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/021.source": "1541b775ca21b78629c8a3358187b651bc59669f3ce806e0e2fef81c0d2199ed", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/022.source": "eb6d4ec2afbbaffce38508a0789a2f12c07f6d7e9bc2c8a8d955ea3f5a60242a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/023.source": "3ef6c54af248bf0debfeb6d2a47f0305fb942f7c68872407486e394da67311df", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/024.source": "d1e8ffdcc1ddedd427fff473728317fbbb5cd493643c02f76f97c72059192be1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/025.source": "212b34b3ca39a811108534bc012eb6c95c2cc47f0bd45355608be4ccbc16e449", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/026.source": "6c34386cd4d3182e2230424a52a56623904f1569e14f1ccfd4de3c5982378fad", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/027.source": "485ad29f46c333c479954b4118d78cd0def72e869236092bbf5cec87b7e51d27", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/028.source": "44965a8e215b4628823c062447863a7916ab57f8eae4c7d38a086159e5762733", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/029.source": "5fb2f3ef9d5d973ea67e93cfee9bf819bcca67bfab2e1c9fc0c60041820f015e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/030.source": "934d98b2d4c28f7a3aae634999faa86bf7d2665e1b3ee921e168756d86c6f536", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/031.source": "ef8803d837942135508520a51788b3785e9fd72a34493f79644c1197f10d556f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/32000/032.source": "4f0238d0211cc89239bb01e674d3044cd5e27aa91d2fc1efdf1781b0dcf014f4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/4000/010.source": "18c9e1ec72927bc57f70d271df8bd2a55e9b1dd6852595b02f63ce83430a4883", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/000.source": "9eb2d52dd04b27bb3b9db2723a9510feade0e1a02233f894b869ea87dbe3660e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/001.source": "b62f2613be37ad6011c1399c13810dc40d7a7d31672ea7b4b845e7388a076652", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/002.source": "a9a0519cc2a6c3a65e7439b10e243ac41e96d1e0b84309ee74cc2ec221ad67e4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/003.source": "6c7ee9d15cf8b902474cb2a889af3e6ce8d930ce6b2549c9370bf917300afb7f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/004.source": "14e3766158a070b4d2458b2d12c46dfed9135e5eb7d92e75cc2ddd267a79df48", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/005.source": "41e6f1b96477603fc758b76907ab2b58c89f5b2a19977a9ae02a977219d043c8", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/006.source": "6e98f4a2cfb379527398e03898a69adcaaf22578436847e3e8e97a2c308b1ee4", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/007.source": "d3229d590c609f7973a948e38c2b535897de1b1a0c4c8155c53b51c200fdd05f", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/008.source": "7baa3aa1ed3dfed62e1f78abedbcf849b2fe24c2d5d98c691f798c299b386cc9", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/009.source": "1ab1ac9656f4ba94ee77a2eadfc583448b7dc9e56810b342d15c4223a49e293a", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/010.source": "1b96890f53d5a7135b7ee8dbd2aa833d315351c53c412ed5f17c04b3717799c1", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/011.source": "4964102336465ac8fda4b73892e06e76450b04c7a6e911bc3923a9ba35bb182e", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/012.source": "6e9579c6e34320ab72c2eb6b62fd2e97b753caffc5fe2fde72d8ee87285bf834", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/013.source": "4f8642971aca332f7c9211a74565ecfd72df1840647e7e697f0a7301f047bb03", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/014.source": "af566e9b84a61be0e5cb7f650727d90d31d354496e61e6f0860b02cd80d2ba15", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/015.source": "f73834c009a6738ec43bdf1f733fac56afcdbf9dbd6bdd6fceacaa6567b6c41c", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/016.source": "30f6edf9f7358667d354a02d8133c3d0b9250877faf97696a64eba0ce9936214", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/017.source": "7b7346b3001bf77da22e762643d47cd5187073ba41ee4eef160d007c5a445b82", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/018.source": "fe91417f99bad942628bf086fc17692a8cc2062fc0a021dc9d75cfdb98864480", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/019.source": "9bc7883443efbda77ee326ec80ec195fc5bc00589d09aedf444dbdd4cde0c118", + "paired-member-focus-01/capture/raw/chi/graphify/windows/source-order/8000/020.source": "4612f1c12f2566386a31823750264c222924baf490d7ad33ef5305792bc6e3b3", + "paired-member-focus-01/capture/raw/click/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/click/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "paired-member-focus-01/capture/raw/click/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/click/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/click/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "paired-member-focus-01/capture/raw/click/compass/mcp/04.request.json": "e148a7f5170d1a8363e3cbaa97110c53950669de602afcc5ef3fd30d2c60c770", + "paired-member-focus-01/capture/raw/click/compass/mcp/04.response.jsonl": "b246a63c4f33e46eed5f15e5789db1bfa54e6ea0eb63c4759a5c4462426d4b21", + "paired-member-focus-01/capture/raw/click/compass/mcp/05.request.json": "4c0ad7366492176ab7c2dd6510655cb151eead3831c1217eb92772f9d1eb4bb2", + "paired-member-focus-01/capture/raw/click/compass/mcp/05.response.jsonl": "b1cfb966e35d68905b7d7f888a689d58a57d162d738d49355b58e7ec0f1a7eff", + "paired-member-focus-01/capture/raw/click/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/name-focus/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/compass/windows/source-order/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/click/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "paired-member-focus-01/capture/raw/click/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/click/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/click/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "paired-member-focus-01/capture/raw/click/graphify/mcp/04.request.json": "4aad9fe4bfc910ea3b74fa2a6bb4335c66ede5da3d24cf48609142507b61a000", + "paired-member-focus-01/capture/raw/click/graphify/mcp/04.response.jsonl": "d22730f840576483c14ecb89c933817acf713e534c6a2b8b8a69ecc7e98714a2", + "paired-member-focus-01/capture/raw/click/graphify/mcp/05.request.json": "faea67c086ba783dd80230e4b0f019e3c07bfef93a31ed288f0f2cec10cc0c03", + "paired-member-focus-01/capture/raw/click/graphify/mcp/05.response.jsonl": "b98212f878f04290fcd5c68ab617bd1cff708c7ba1b23dd9d277a584b595bb72", + "paired-member-focus-01/capture/raw/click/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/000.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/001.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/002.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/name-focus/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/16000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/2000/006.source": "8fa9d06237cf26939e2c2e30c99bff71e0c5bb6e4207fb596e7cf754952e2fb0", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/32000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/4000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/000.source": "75e95b9bbc3c0e0eb155a8832aece6c91ad55fc665f3e75f975f7f56a4f714c4", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/001.source": "d60bde99d994e7a7514909d64528e29bca38a4a8ab83539fd5255f78b55e8e9b", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/002.source": "ccddee379cb2066c45d953575817cc4039d73deab55149c3378717de58b76632", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/003.source": "6c48735e9c2665c3f370a0dc40392d32c969bca52d33e3e4dc6d42dad7c1e48a", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/004.source": "093b12bc46f9d5036f4b4dbaa6d3a0846aa191ba19b826d415077363a304c3fa", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/005.source": "6069237b3debc11dd51020c834ad9e5816bbeab1000466034710d575faf439d1", + "paired-member-focus-01/capture/raw/click/graphify/windows/source-order/8000/006.source": "29d44ac5a6a9f8ded9079cf6c8315259bf1d21a487df8993d48d575bd5dc92b8", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/04.request.json": "88321b0cefc99d73f5f5bab430106fa357e7ce37ea60d7273e573f6caf186d36", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/04.response.jsonl": "c88e7e11ddfca95460f1ba73fd91d2ff376914106db214ad87cc62253d4410fc", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/05.request.json": "ff3fcdd774e4493d27f81784e37e91f956cbecc7f07e434c96ec212f96bf2280", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/05.response.jsonl": "875ee5125b384d6f9e66d56e231c491c8959d95014b256f08253ea5a8e483b0a", + "paired-member-focus-01/capture/raw/jsoup/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/003.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/004.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/005.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/16000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/2000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/2000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/2000/002.source": "40d819bc77d5b77456459f75c607adb415e4c362c627f48a9546d8cb44a3b87d", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/003.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/004.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/005.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/32000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/4000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/4000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/4000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/4000/003.source": "2cd05caa4260c9de8676a872dd1ab3ea4756585fdff58ce508ad99e1d8240b42", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/003.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/004.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/005.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/name-focus/8000/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/16000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/2000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/2000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/2000/002.source": "f001d1fcca3d1fcb243f116312615cc01d807a28d05dfc3cdf4b8b0287ebc490", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/007.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/32000/008.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/4000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/4000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/4000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/4000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/4000/004.source": "426592e2fb2b4d671f607a34bc6d5eedfe454775ce3304674af04738bdd220a2", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/000.source": "f1137f4d17d7bc9a18934c4387a08fd79118e7f98548d4e9ac73a3edd6cd237c", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/001.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/002.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/005.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/006.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/compass/windows/source-order/8000/007.source": "c80f32af2fa6b5b37f6a6b6aed035cc318e7d4e6bda0580a428bff234b2ac17a", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/04.request.json": "883da0706262e806f7254d1763f680dbebb7917ab916867d30c4f43b2ae6b8b9", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/04.response.jsonl": "7ac5d473f020a3eef7e5f7aeb277fa5b33784b888f2d83cdb791ee569b66ec11", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/05.request.json": "722d113b0a110540d31515b29f0c07c5ef96f04d5bf471ffb26605db2cd493ff", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/05.response.jsonl": "b3f990a6d8fc01f0d15ca9272e7c95e8ad376f2a4dae010105662903f08d3896", + "paired-member-focus-01/capture/raw/jsoup/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/16000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/2000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/2000/001.source": "88dc87722aeb25b94e9d65aca2fba435de9549e5b610468138f69fad1007f03b", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/32000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/4000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/4000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/4000/002.source": "037358b45081373a57ef7ca4a6a5576d9eaa78b5f1e53b268dfcb7788fa2c308", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/002.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/003.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/004.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/name-focus/8000/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/16000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/2000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/2000/001.source": "88dc87722aeb25b94e9d65aca2fba435de9549e5b610468138f69fad1007f03b", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/006.source": "2a0e8ae288a3240c008c9016181a88ca014bd1b1cd9cee9f30b0a2c44f9cd4b1", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/32000/007.source": "c46a498a7cdc21c07947545e231ddcaaa0678abb7c78a36e5620069e6b250c56", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/4000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/4000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/4000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/4000/003.source": "92f42d96bb1e12fff294e506bf96320736381f55b60044a2e0fcaa394d7a7e66", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/000.source": "b577f811b284f994dc62cfd95d6c60d63e5d5b70cac13cb5cb3da8ba175b8e71", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/001.source": "39bd7723e9a79009a4a2aeb394f195913bf9f10ef0dc062a4c2f7af545134689", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/002.source": "4b46b8429cab31bbc393bf838692c23b5bce68cf1809a07982380fcfac468a11", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/003.source": "5fca2e4eae89593e8bfba5cd9b0d10f8269493abd2a6f08c49940c0cce1f49a7", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/004.source": "618a9b93563047fcf224a5c27946d83af48606455a610d785c9675ab93527589", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/005.source": "eb5ce17dd624ef1c4474e74a29cf9013c9cae2eff77a75e8560ba06c6b085a28", + "paired-member-focus-01/capture/raw/jsoup/graphify/windows/source-order/8000/006.source": "655f66ae9b5a39084ace665e8c4bc2908062931d8d7a8d4370ae0af6e4792230", + "paired-member-focus-01/capture/raw/redux/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/redux/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "paired-member-focus-01/capture/raw/redux/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/redux/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/redux/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "paired-member-focus-01/capture/raw/redux/compass/mcp/04.request.json": "8803daedf87b9bd35456accb3487cc9e3491a392638e95b56a3d55f8e66f89d5", + "paired-member-focus-01/capture/raw/redux/compass/mcp/04.response.jsonl": "f44f175ad81c0379dd06e156a504fb1f3ceb8e31bd4ddf62777feabdc0700b8c", + "paired-member-focus-01/capture/raw/redux/compass/mcp/05.request.json": "f14c1893f73d7dc8de473fa0cf98d94ca628c56f486b2eb9f9d3f062f42fc479", + "paired-member-focus-01/capture/raw/redux/compass/mcp/05.response.jsonl": "52c6b1012ea90597e06714535dacfd36b80c47132d41501ec6db928751620cc0", + "paired-member-focus-01/capture/raw/redux/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/000.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/001.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/002.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/003.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/004.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/005.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/006.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/007.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/008.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/009.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/010.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/011.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/012.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/013.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/014.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/015.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/016.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/017.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/018.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/019.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/020.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/021.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/16000/022.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/2000/000.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/2000/001.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/2000/002.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/2000/003.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/2000/004.source": "c7e912f3138f2ff7e5b687e1532c3a2561c5808274c01307d80762eb8566f615", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/000.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/001.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/002.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/003.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/004.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/005.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/006.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/007.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/008.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/009.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/010.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/011.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/012.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/013.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/014.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/015.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/016.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/017.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/018.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/019.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/020.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/021.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/32000/022.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/000.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/001.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/002.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/003.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/004.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/005.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/006.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/007.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/4000/008.source": "4b226d5c5bb534e839d6db3499485600f647137a9f536395e2d3fe76ce232de6", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/000.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/001.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/002.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/003.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/004.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/005.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/006.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/007.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/008.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/009.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/010.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/011.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/name-focus/8000/012.source": "8026bc2fbb69952096b7136fa97b4360d2bb84d662e313797872d30fcb73bb47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/015.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/016.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/017.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/018.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/019.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/020.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/021.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/16000/022.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/2000/011.source": "1bb164faae3ccfe7de42a98c03dbd69039a01c9cd6741b48f9b017d0d583ddac", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/015.source": "8fdb35987b453edb7790f834259829503f8198d58ef579224df9fd4517da2639", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/016.source": "a953ead67ac621be50be7865aa5126243ba01f2aff770cbe69f1af5549683196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/017.source": "4757864d63eee59db076a53611238958606303997f9a6275d5aa54a6be06b4a4", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/018.source": "86cf9d28574843366105776ab0a44ee3a3ae791d1f7e090d64d159b6ee1ea3fe", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/019.source": "664104220b590744932c6f2b032be0cd60ae9dc02e32b24841c006400675d5f1", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/020.source": "c4a60c00a9c6b8b4c772f637dae7d21634838abf8b3110018e233fda6dd62b02", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/021.source": "d2e88b5804619ae89b7b5e5a754917fb44638ec1f45fd662c9d5bd8696a9cb7d", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/32000/022.source": "297cd3652a03e447c7e6176c8469bbbe1d8a37b4e06c008db06c77f625e859e9", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/4000/014.source": "f991a26b534be8084f3b1bb9067c7031357eb3c52bc9adcb9f504010fb133047", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/000.source": "fefd5fb81a7b23445d2f150789cf0f05109f23106acdb2f980624e739e01bac2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/001.source": "1c7fe407a6684530c4e4d4f4f1c306bfa03a3cdcfd928a788fbf90b7eb8c9196", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/002.source": "ff066db901fe9a115af900172fa4195da1031fca12ed2e4f7fe00e426f6a2943", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/003.source": "bcf54065989a31c0d240046050b2f438c5d6f4da6e8e240c13d996f463b6c888", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/004.source": "bcc7dbea05f05d97162aa993cc72a73bea8ce4a97c7b1fb65a5964a14bf670f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/005.source": "c126acce74d43261c4d8f6aabcf8edc0b962c09ce4f75f36cf18f96214e23e47", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/006.source": "ff2309baa17a76ad34adfe7c0c62241739a35ef30bfbe8bcc6f44725c5f5ed84", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/007.source": "2f01b1f5e664017b7dbfe07eec58a8a44e699fba4defd0f976c543ca622785b8", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/008.source": "07389aee59f45563b27f911b823055ee3132b091d6d4bdb04f7f2fb8b3d88206", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/009.source": "ea600cae25f2f26c93cec0be4b3956d94f1e3795befdf89b0148d60498e51ca2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/010.source": "09eb4afe0fee0721cbe28d517e7a376aaf5de502d8c1a95300adc7253af7d8f2", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/011.source": "74bc223cde432e77b8936141da3a79860da4c238b719bdee2fd1de08ce3108a7", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/012.source": "6ede28da15d093f8beced8ee23d475fc3e7b6fbe465228108ca0aff4d50a7d7e", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/013.source": "0e16e43c7405bb30498120b98523fbf3fb06d7f4389131f3b3f8ec392f6945ef", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/014.source": "7421a463ba9ee7d106d36ab2812719069881eaeb4aa38820e4b15f3d771d450f", + "paired-member-focus-01/capture/raw/redux/compass/windows/source-order/8000/015.source": "2fa50459f3adaa2c60d3d328d13ddb749207e27feb3c92fe43812e9f2edc01c6", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/04.request.json": "c32cfb2450c017920145bae06b04af6f131c02499e30d987f76daf8e53f1a5e9", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/04.response.jsonl": "cba36e66dcc88f9313662b40b072d5c24cf9fbf25310ac3b1c2f4eaebc8e02c6", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/05.request.json": "f38f6d67ccd3f8f0281ba3709abeaab237da52011550ad8bd0bd1a9fa035a6b1", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/05.response.jsonl": "0ec3d11ad7c82aab1979ae0fe8cd9dc1622478bc3a24e0898627e61b9636769b", + "paired-member-focus-01/capture/raw/redux/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/16000/000.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/16000/001.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/2000/000.source": "f461b5313d734e1293e99e20e61436ca9feb668b1f12e957a2465f11cce45a4d", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/32000/000.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/32000/001.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/4000/000.source": "c1968e9c558cb1a0c01df4ae08d11d88594bb54f918a95388d1d38f9f625f41e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/8000/000.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/name-focus/8000/001.source": "361550eb020b213966a4f9e828905c27c86b2be5455b0ed66d8c541b4802fc21", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/16000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/16000/001.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/2000/000.source": "718759fe6d1d524a045503d49b4d6af5791f62227270ed358a0c8efd1cb2c0e8", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/32000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/32000/001.source": "207289d44efe42419f6d7493db434d0417b5247dd9b87cd5ce6d3db29f664f7e", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/4000/000.source": "6ac89178efb4169df169a238447bcdb738c5a8f6d926212dded707cedc73ea22", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/8000/000.source": "02f3df4750372f7d2806a2932dcb7a287ac405470fdbaf58c96adb8903f7223c", + "paired-member-focus-01/capture/raw/redux/graphify/windows/source-order/8000/001.source": "104842ff0af72b77928d0a849bc5aa5e5bf080e95aeca8f8bc0baacd3dd90aa1", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/01.response.jsonl": "444f8278167864389038bc2b79fa4620d30cec4d51a6cd729797a86d487f1d23", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/03.response.jsonl": "9dee655ad44513e766155ad52de42e306ff48b3965dc25726bdc84796a4dbfc2", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/04.request.json": "d339f2ef2ad14a8ba3ccfaeed06102dae89e51ee863ddda562fcef92e1262bd2", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/04.response.jsonl": "28624be871949873b0f55b789ce3549ba5c966eeca09c89ccb526adc0607fa00", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/05.request.json": "75e03f39e5544dca970a2ed6183ee7389eaab84a0edf9b41c9ed3aead0a0b2f5", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/05.response.jsonl": "2b14975c56bdfab6f4d8926511462d5603fc9b83dfffe030cbdb8e6c3b11610f", + "paired-member-focus-01/capture/raw/walkdir/compass/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/000.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/001.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/002.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/003.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/004.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/005.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/006.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/007.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/008.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/009.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/010.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/011.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/012.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/013.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/014.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/015.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/16000/016.source": "85d302b6beedc991c4291c96ce94f11a461cef9f96755db330354fb36f024328", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/000.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/001.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/002.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/003.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/004.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/005.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/006.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/2000/007.source": "36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/000.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/001.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/002.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/003.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/004.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/005.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/006.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/007.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/008.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/009.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/010.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/011.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/012.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/013.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/014.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/015.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/016.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/017.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/018.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/32000/019.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/000.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/001.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/002.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/003.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/004.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/005.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/006.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/007.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/4000/008.source": "b83a56a9e29e7e04cd65a2cf7f565ef5e20ac6dc290c887d4e36d72bf52e3e11", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/000.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/001.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/002.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/003.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/004.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/005.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/006.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/007.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/008.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/009.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/name-focus/8000/010.source": "e6ab0dd7e91decb330ea7b038f83d206bec726df3f9874532f81adadcbf57619", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/009.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/010.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/011.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/012.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/013.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/014.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/015.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/16000/016.source": "6e64bcf58026de82970666d4b4db45e862854dac48782a6034087bc047263137", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/2000/007.source": "cbbf1bd40fef95750f8722ee53276dca4d5289613e8082eb030a14236d235ed0", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/009.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/010.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/011.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/012.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/013.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/014.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/015.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/016.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/017.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/018.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/32000/019.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/4000/007.source": "b01050c7b77958949f0ba035e4783dc6e44688bd1937800531564e275cfb29f6", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/000.source": "ff45ca1c29db9f3173b1f545ef952fb6b79c3fe1bc0bd48c6948fbbf216fd72a", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/001.source": "386a59fc2a5e4ae1f44959b2be8feeb2ac6dde5822d24f36c4165bfdfeb417ac", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/002.source": "2e584390c911c511c659505ad05cf56eb6f2124bd3d365715d47b80b352f4f99", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/003.source": "81998d6b94d50a7e4bbe3bfa2df65cb1a331098c70eb327ac33c8fd66974b0fb", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/004.source": "cfa77a0ba1f9e61f2c03f2754f13a698331b406e0b76623484aaac2b96db4831", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/005.source": "2b1252e1e1bcb53d4f0a66e59ced59dbb29926ae626b78cea2ecaeaa39d5a276", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/006.source": "cc91b2d0560786f75b77509e46eb2b04a0fa4242464c1705e05b91f11a7d9e64", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/007.source": "e40e140647193c41d7635036b1dec0786efe1cbc5863edaf76db8096fe8d4ddf", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/008.source": "be7342201ed171b2aa6ef80130e3b3d8ec259442a137c0fa9a3f5182ee79401e", + "paired-member-focus-01/capture/raw/walkdir/compass/windows/source-order/8000/009.source": "907ef6bd190a9985da364ec084579cacae6b06067d5a3bdfa8d98a2c3256eb9b", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/01.request.json": "c7a933c9b57fcff6957c6dbe624b6443d1a62b28bebbf4445452a117a146d2e5", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/01.response.jsonl": "e38aa737515685d5e80f14b4ae13b09ac47cc287e64c0872d9a2129b6db2c1f5", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/02.request.json": "0f1af7b35815b407111bc7d97d0c26b1b879311df49455ef2a676ff20cecbb38", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/03.request.json": "cea371b903cf1632df866b3cdc4941a42b631907b8fbbebff01a52d845e65cdd", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/03.response.jsonl": "58f4bfd56df06a7801ad440723d137cb8510049e5192f77b15ac79700380df01", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/04.request.json": "ac9506fc18f6f6cf468a46a9e781188b01156b4b7c6ab64ad601ab06640a8c25", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/04.response.jsonl": "2190ecdb65f6a8ba2da85c820eccc0fdd4f5c647cedd341f1d624e1368c0977e", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/05.request.json": "18b4ad53dd232900c7db7d584dd42b068d0d3e27cd663c34c4433b6ad5b89197", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/05.response.jsonl": "ea3873a259a5818f1f61c5cef724df012ca38927954973acd3c2e8ff860c2585", + "paired-member-focus-01/capture/raw/walkdir/graphify/mcp/stderr": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/000.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/001.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/002.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/003.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/004.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/005.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/006.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/007.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/008.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/16000/010.source": "6200f361738587d8826b72cc779afd1b8d11b135feaa3b98377ead3d14218764", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/2000/000.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/2000/001.source": "95be5f499b8d80faef8eabfca1866736287da731e2d1dc75f4e5d8ec403a5fe0", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/000.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/001.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/002.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/003.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/004.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/005.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/006.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/007.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/008.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/32000/010.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/4000/000.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/4000/001.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/4000/002.source": "0aad7da77d2ed59c396c99a74e49f3a4524dcdbcb5163251b1433d640247aeb4", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/000.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/001.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/002.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/003.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/004.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/name-focus/8000/005.source": "665a76f23d682ab4c026686aac6c9877fa67b88bf3f427408ea82c121739da7a", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/005.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/006.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/007.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/008.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/16000/010.source": "6200f361738587d8826b72cc779afd1b8d11b135feaa3b98377ead3d14218764", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/2000/000.source": "e9afda558aa648a1626f292d9445290945caf90d6eaf7af24e1a9f32e032118a", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/005.source": "1c1d979dfbe779e90ee6fc9c6179e172a4abde0c5958fbb818598ae752775248", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/006.source": "167e0576debe36584c4cacfc9f5bbd2ee31036aa7020e79f0f8b7518df806058", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/007.source": "73920176fac6c226d78800a2c09e7edab016a662796fca44eb64102a7269849e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/008.source": "49af4ac0821c254d74cb34f99806c400b3303e3646e4ed615bf60b12051a756e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/009.source": "b71caf66004b029b7c98c5f78d373a7c78ab1a9f7b6bfa10a4d6b876f1957af6", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/32000/010.source": "d4f9d4bcbdd802b0a3b9865ec683a6a16ae0752470aea7878e9fa03fee02fbea", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/4000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/4000/001.source": "fb4eb23be72cc1c60225692eb7bf38b91a4aea2f2b830a4ae4c9063b853f37cf", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/000.source": "47cab2eca2cc07d78d319f7a9acc39c45d31992cc01788c804c2f9b53ff3600b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/001.source": "c08a2af053b0f567c208bc02679f7d0442c7d867050104de90edb223a2cab75b", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/002.source": "64f8be64c7277d8447281854e37989c0b4d002448950982be85d036fdb8a1a7d", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/003.source": "c1e268fb5bde75384cb45c0375ade42dda153da149dfa7645f53d67854c7672e", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/004.source": "2923912d8a93208e4633fd4efd61514a7449defd6519f815c445be6318c10f32", + "paired-member-focus-01/capture/raw/walkdir/graphify/windows/source-order/8000/005.source": "b0c0b5e23f7d4cf98d355ff7f08101ea772f51b09b278f51ab7bb66ad2aaf4a6", + "paired-member-focus-01/capture/runner.py": "4616b4c3a13e17febad2851c6111bff61e7b35e216fabf481df281380cddcebb", + "paired-member-focus-01/capture/source_windows.py": "8511773acb84c3d7ae19a94f904a0a0422a32b0cafc1cfee15f9725b94e5ab43", + "paired-member-focus-01/capture/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "paired-member-focus-01/capture.log": "bf14c30211b4674a7357f459fc5f91b25d45ed3c739959ac1cdeb7091f77ea84", + "paired-member-focus-01/capture.py": "07d0f062dd38dc5469aea24c0eeeae921bbc9fce29be3d0134e75d54a3e42edc", + "paired-member-focus-01/prepare_verifier.py": "04c4c0b32071427a7dcd1709ef4bd01d0bf4a70c45c9f5d69ba13f83cdd0b240", + "paired-member-focus-01/product-boundary.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "paired-member-focus-01/redux-window-diagnosis.json": "52f27e5d4235560b89e1745e073f8898db12b4dff616e3912a880b7a2ec6452d", + "paired-member-focus-01/repeat-verification.log": "ae5f8cbd3f578b2a75af5203cd791b9e18ad64dec48628d122f18b36806d4b50", + "paired-member-focus-01/replay.json": "1e7c5e3332c608a3d7f888a2890da82e0a110deb3bea1ec6767cd8acc7658e72", + "paired-member-focus-01/verification.log": "ae5f8cbd3f578b2a75af5203cd791b9e18ad64dec48628d122f18b36806d4b50", + "paired-member-focus-01/verified-summary.json": "683ae9490003eb18c55c58142088418c36ef91fc8bb58bb2a447d9029d1cd7c5", + "paired-member-focus-01/verify.py": "5d59fb9a24716ba2c276781b85b1aec66e0fd46d96b3aae234e2d5bfbb9bb269", + "paired-member-focus-01/window_policy.py": "b81cd0a57472dfe74a41eff8ee70dd8492fcf627f255f9c5dc88877628d94b94", + "paired-member-focus-01/write_review.py": "d20fe529164a89ab2bb3bab3a11d74c0b6cdfef2e7767a243a74db60fde414ad" + } +} diff --git a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md index 7ddcfbb49..fa684c45f 100644 --- a/docs/implementation/code-graph-intelligence-audit-2026-09-26.md +++ b/docs/implementation/code-graph-intelligence-audit-2026-09-26.md @@ -3301,6 +3301,91 @@ remains 0.3.30; no graph schema, extraction, cache or history changes occur. Authored answers, fair common-workflow focus comparison, longer walks, god-object judgments, functional communities and held-out confirmation remain open. +## Common member-name focus: paired negative result + +Registration `67e5550b` fixes a symmetric public-interface experiment after the +native-only improvement above. Policy and tests were committed as `6c9876ee` +before fresh captures. Both tools receive the same exact identity constraints, +full questions and five source quotas. Each subject uses one resolver call and +one neighbor call. The same helper ranks returned outgoing `contains`/`method` +labels by distinct normalized question-term matches. A shared file/line group's +score is the maximum score of one of its labels; duplicates and alternative +labels cannot accumulate weight. Source order breaks ties and unmatched groups +remain eligible. Fields, bindings and nested types are retained when returned by +either tool; no Compass-only kind filter or private graph metadata ranks them. + +The source intervals are fixed **before** ranking: next greater returned anchor +in the same file, or the existing 4,096-byte final-window cap. Reranking changes +visit order, never interval ends. The same raw-byte quota applies to both tools. +The original source-order controls are captured alongside the focused arm. + +| Source quota per subject | Source order: Compass | Source order: Graphify | Common focus: Compass | Common focus: Graphify | +| --- | ---: | ---: | ---: | ---: | +| 2,000 | 8/20 | 8/20 | 6/20 | 8/20 | +| 4,000 | 8/20 | 8/20 | 9/20 | 10/20 | +| 8,000 (primary) | 14/20 | 15/20 | 12/20 | 14/20 | +| 16,000 | 19/20 | 18/20 | 19/20 | 18/20 | +| 32,000 | 20/20 | 18/20 | 20/20 | 18/20 | + +**Reject this common name-first window policy as the default explanation +retrieval policy.** Its primary result regresses for both tools, and Compass +still trails Graphify. The favorable 4,000-byte result cannot replace the +registered primary result. This does not invalidate the separate native +callable-span 14/20 to 15/20 experiment; it shows that the improvement does not +transfer reliably to a different evidence layout. Native focus remains optional. + +At 8,000 bytes: + +- Both tools gain Chi routing fact `chi-4` but lose the `With` shared-state fact + `chi-1`. Seven matching routing/middleware anchors move first. The existing + query normalization removes `With` as a stopword in the full question, and its + unmatched window no longer fits. The unchanged total conceals this tradeoff. +- Compass loses Redux enhancer delegation (`redux-1`) and listener snapshot + semantics (`redux-3`). A matching `getState` binding at line 390 promotes a + 3,886-byte final window ending at line 489. Post-capture inspection of the + owner span shows that **3,759 bytes follow the end of `createStore` at line + 395**. That owner metadata was not used to select, clip or score the window. + A name hit can therefore spend most of the quota on subsequent source. +- Graphify loses WalkDir contents-first evidence (`walkdir-4`). Promoting the + 606-byte `check_loop` window leaves the `get_deferred_dir` window partial. + Neither tool gains the complete multi-method loop-handling fact at this quota. +- Click and jsoup have no fact changes at this quota. The existing Click + class-header allowance is preserved; literal totals remain one lower per tool. + +All actual retained-source totals equal the corresponding source-order totals; +no extra source quota explains these changes. At the primary quota each tool +retains 35,673 bytes across five subjects. Public response costs remain unchanged +and unequal: Compass resolver 3,039 text / 21,983 wire bytes and neighbors +33,061 / 372,871; Graphify resolver 619 / 1,094 and neighbors 6,980 / 7,534. +These are source-evidence scores, not authored answers, equal-token comparisons +or a claim of overall superiority. + +All 20 fresh public responses and their 153 membership anchors reproduce the +prior capture. All 50 source-order window/scoring arms match prior results. +A separate same-agent verifier independently checks normalization on this ASCII +question/label panel, every group score, all 100 ordered-window arms, raw source +bytes, transcript costs, witness judgments and historical comparisons. Repeated +verification is byte-identical. The shared helper also has Unicode tests; no +universal cross-runtime normalization equivalence is claimed. + +All **202 benchmark tests and the product boundary pass**. Fifteen new tests +cover frozen lexical constants, prior native question-term agreement, Unicode, +snake/camel case, duplicate terms and labels, maximum-not-union group scores, +source ties, ignored tool metadata, bounds, exact group permutations and fixed +source interval ends. No production code changes in this checkpoint; native +Rust, JavaScript, platform and packaging checks were not rerun. Product commit +remains `293582c3`, package version 0.3.30. + +`paired_member_focus_review.json` publishes every quota, subject, gained/lost +fact, actual cost and provenance hash. External `paired-member-focus-01` retains +fresh MCP transcripts, ranked group traces, 100 source-window arms, the Redux +boundary diagnosis and capture/replay scripts. The next retrieval work needs +accurate available source extents, preservation of explicitly named symbols, +and enough linked implementation evidence for multi-method facts. It must be +qualified under a newly registered common workflow, preserving this negative +result. Longer paths, authored answers, functional communities, actual god-object +judgments and held-out confirmation remain open. + ## Next evidence to collect 1. Re-review the invalidated pinned hierarchy scorecards from their sources.