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;
- backend: producer and mode;
- 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
@ -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
concrete-witness confidence must remain distinguishable. Binary evidence must
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.
5. Duplicate names use stable qualified identities and do not resolve
ambiguously.
6. Every edge retains backend, confidence, and source evidence. If multiple
source locations are collapsed, the evidence must say so.
6. Every edge retains backend, confidence, its first provenance value, and all
distinct source locations contributing to a deduplicated relationship.
## Parity test contract

View file

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

View file

@ -72,6 +72,8 @@ pub struct Evidence {
pub backend: String,
pub confidence: f64,
pub provenance: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub locations: Vec<String>,
}
#[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 {
let provenance = provenance.into();
Evidence {
lane: EvidenceLane::Syntactic,
backend: backend.into(),
confidence,
provenance: provenance.into(),
locations: vec![provenance.clone()],
provenance,
}
}

View file

@ -531,8 +531,17 @@ impl ScanState {
confidence: f64,
provenance: impl Into<String>,
) {
let provenance = provenance.into();
let key = (source.clone(), target.clone(), kind);
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;
}
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]
fn scans_dart_structure_without_promoting_control_flow_or_strings() {
let dir = tempfile::tempdir().unwrap();