feat: establish first-class lexical language parity #2

Merged
trevon merged 14 commits from feat/language-parity-foundation into bench/yams-architecture-reproducibility 2026-08-30 22:09:40 +00:00
5 changed files with 59 additions and 6 deletions
Showing only changes of commit 5f3b2484cc - Show all commits

feat(model): retain all dependency evidence locations

trvon 2026-08-30 15:39:08 -06:00
Signed by: trevon
GPG key ID: 38686EA797D7A8A7

View file

@ -26,7 +26,10 @@ Every edge has:
- lane: syntactic, symbolic, imported, or heuristic; - lane: syntactic, symbolic, imported, or heuristic;
- backend: producer and mode; - backend: producer and mode;
- confidence: producer-calibrated value; - confidence: producer-calibrated value;
- provenance: source location or report artifact. - provenance: the first source location or report artifact, retained for v0.1
consumers;
- locations: every distinct source location contributing to a deduplicated
edge.
## Health indicator ## Health indicator
@ -50,4 +53,3 @@ should map Fold function/block/operation/value fact IDs directly to Brick
identities and retain source/binary lanes. Source proved, latent, heuristic, and identities and retain source/binary lanes. Source proved, latent, heuristic, and
concrete-witness confidence must remain distinguishable. Binary evidence must concrete-witness confidence must remain distinguishable. Binary evidence must
not be promoted to symbolic evidence without corroboration. not be promoted to symbolic evidence without corroboration.

View file

@ -35,8 +35,8 @@ All extractors must satisfy these rules before first-class parity is claimed:
or calls. or calls.
5. Duplicate names use stable qualified identities and do not resolve 5. Duplicate names use stable qualified identities and do not resolve
ambiguously. ambiguously.
6. Every edge retains backend, confidence, and source evidence. If multiple 6. Every edge retains backend, confidence, its first provenance value, and all
source locations are collapsed, the evidence must say so. distinct source locations contributing to a deduplicated relationship.
## Parity test contract ## Parity test contract

View file

@ -57,6 +57,7 @@ pub fn import_findings(building: &mut Building, report: &Path) -> Result<usize>
lines_of_code: 0, lines_of_code: 0,
attributes, attributes,
}); });
let provenance = format!("{}:{}", report.display(), finding.line);
building.edges.push(Edge { building.edges.push(Edge {
id: stable_id("edge", &format!("{node_id}:flags:{file_id}")), id: stable_id("edge", &format!("{node_id}:flags:{file_id}")),
source: node_id, source: node_id,
@ -70,7 +71,8 @@ pub fn import_findings(building: &mut Building, report: &Path) -> Result<usize>
}, },
backend: format!("fold:discover:{}", finding.backend), backend: format!("fold:discover:{}", finding.backend),
confidence: confidence_value(&finding.confidence), confidence: confidence_value(&finding.confidence),
provenance: format!("{}:{}", report.display(), finding.line), locations: vec![provenance.clone()],
provenance,
}, },
}); });
imported += 1; imported += 1;

View file

@ -72,6 +72,8 @@ pub struct Evidence {
pub backend: String, pub backend: String,
pub confidence: f64, pub confidence: f64,
pub provenance: String, pub provenance: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub locations: Vec<String>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@ -142,10 +144,12 @@ pub fn stable_id(kind: &str, identity: &str) -> String {
} }
pub fn evidence(backend: &str, confidence: f64, provenance: impl Into<String>) -> Evidence { pub fn evidence(backend: &str, confidence: f64, provenance: impl Into<String>) -> Evidence {
let provenance = provenance.into();
Evidence { Evidence {
lane: EvidenceLane::Syntactic, lane: EvidenceLane::Syntactic,
backend: backend.into(), backend: backend.into(),
confidence, confidence,
provenance: provenance.into(), locations: vec![provenance.clone()],
provenance,
} }
} }

View file

@ -531,8 +531,17 @@ impl ScanState {
confidence: f64, confidence: f64,
provenance: impl Into<String>, provenance: impl Into<String>,
) { ) {
let provenance = provenance.into();
let key = (source.clone(), target.clone(), kind); let key = (source.clone(), target.clone(), kind);
if !self.edge_keys.insert(key) { if !self.edge_keys.insert(key) {
if let Some(edge) = self
.edges
.iter_mut()
.find(|edge| edge.source == source && edge.target == target && edge.kind == kind)
&& !edge.evidence.locations.contains(&provenance)
{
edge.evidence.locations.push(provenance);
}
return; return;
} }
let identity = format!("{source}:{}:{target}", edge_kind_name(kind)); let identity = format!("{source}:{}:{target}", edge_kind_name(kind));
@ -1038,6 +1047,42 @@ mod tests {
})); }));
} }
#[test]
fn retains_every_location_for_a_deduplicated_dependency_edge() {
let dir = tempfile::tempdir().unwrap();
fs::write(
dir.path().join("calls.rs"),
"fn helper() {}\nfn caller() {\n helper();\n helper();\n}\n",
)
.unwrap();
let building = scan(dir.path()).unwrap();
let caller = building
.nodes
.iter()
.find(|node| node.kind == NodeKind::Function && node.name == "caller")
.unwrap();
let helper = building
.nodes
.iter()
.find(|node| node.kind == NodeKind::Function && node.name == "helper")
.unwrap();
let call = building
.edges
.iter()
.find(|edge| {
edge.kind == EdgeKind::Calls && edge.source == caller.id && edge.target == helper.id
})
.unwrap();
let serialized = serde_json::to_value(&call.evidence).unwrap();
assert_eq!(call.evidence.provenance, "calls.rs:3");
assert_eq!(
serialized["locations"],
serde_json::json!(["calls.rs:3", "calls.rs:4"])
);
}
#[test] #[test]
fn scans_dart_structure_without_promoting_control_flow_or_strings() { fn scans_dart_structure_without_promoting_control_flow_or_strings() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();