use std::collections::BTreeSet;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReleaseGapStatus {
Open,
InProgress,
Fixed,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ReleaseGapFinding<'a> {
pub id: &'a str,
pub owner: &'a str,
pub feature: &'a str,
pub reproduction_command: &'a str,
pub evidence_path: &'a str,
pub status: ReleaseGapStatus,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ReleaseGapFindingProof {
pub gap_count: usize,
pub open_count: usize,
pub fixed_count: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ReleaseGapSuiteArtifactProof {
pub file_count: u64,
pub vyre_file_count: u64,
pub dataflow_consumer_file_count: u64,
pub vyrec_file_count: u64,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ReleaseGapFindingError {
EmptyFindings,
DuplicateId {
id: String,
},
EmptyMetadata {
id: String,
field: &'static str,
},
ReproductionDoesNotUseCargoFull {
id: String,
command: String,
},
NoActiveGap,
ArtifactMissingEvidence {
evidence: &'static str,
},
ArtifactMissingNumber {
field: &'static str,
},
ArtifactThresholdMiss {
field: &'static str,
observed: u64,
required: u64,
},
}
impl std::fmt::Display for ReleaseGapFindingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EmptyFindings => write!(
f,
"release gap findings are empty. Fix: track every missing clang/dataflow parity item as an explicit gap finding."
),
Self::DuplicateId { id } => write!(
f,
"release gap finding `{id}` is duplicated. Fix: keep one tracked owner per gap."
),
Self::EmptyMetadata { id, field } => write!(
f,
"release gap finding `{id}` has empty {field}. Fix: every gap needs owner, feature, reproduction command, evidence path, and status."
),
Self::ReproductionDoesNotUseCargoFull { id, command } => write!(
f,
"release gap finding `{id}` uses reproduction command `{command}` instead of ./cargo_full. Fix: make the gap reproducible through the release test harness."
),
Self::NoActiveGap => write!(
f,
"release gap findings contain no open or in-progress gaps. Fix: if parity is complete, convert this gate into fixed regression evidence after completion audit."
),
Self::ArtifactMissingEvidence { evidence } => write!(
f,
"release gap suite artifact is missing {evidence}. Fix: commit reproducible gap-suite evidence rather than synthetic gap inventory."
),
Self::ArtifactMissingNumber { field } => write!(
f,
"release gap suite artifact has no numeric {field}. Fix: record the exact gap-suite counter."
),
Self::ArtifactThresholdMiss {
field,
observed,
required,
} => write!(
f,
"release gap suite artifact {field}={observed} missed required {required}. Fix: add real gap tests across Vyre, dataflow consumers, and Vyrec or close the approved beta gap explicitly."
),
}
}
}
impl std::error::Error for ReleaseGapFindingError {}
pub fn validate_release_gap_findings(
findings: &[ReleaseGapFinding<'_>],
) -> Result<ReleaseGapFindingProof, ReleaseGapFindingError> {
if findings.is_empty() {
return Err(ReleaseGapFindingError::EmptyFindings);
}
let mut ids = BTreeSet::new();
let mut open_count = 0_usize;
let mut fixed_count = 0_usize;
for finding in findings {
validate_metadata(finding)?;
if !ids.insert(finding.id) {
return Err(ReleaseGapFindingError::DuplicateId {
id: finding.id.to_owned(),
});
}
if !finding
.reproduction_command
.trim_start()
.starts_with("./cargo_full ")
{
return Err(ReleaseGapFindingError::ReproductionDoesNotUseCargoFull {
id: finding.id.to_owned(),
command: finding.reproduction_command.to_owned(),
});
}
match finding.status {
ReleaseGapStatus::Open | ReleaseGapStatus::InProgress => open_count += 1,
ReleaseGapStatus::Fixed => fixed_count += 1,
}
}
if open_count == 0 {
return Err(ReleaseGapFindingError::NoActiveGap);
}
Ok(ReleaseGapFindingProof {
gap_count: findings.len(),
open_count,
fixed_count,
})
}
pub fn validate_release_gap_suite_artifact(
artifact: &str,
) -> Result<ReleaseGapSuiteArtifactProof, ReleaseGapFindingError> {
artifact_contains(artifact, "gap suite marker", "\"suite\": \"gap\"")?;
artifact_contains(artifact, "zero blockers", "\"blockers\": []")?;
artifact_contains(artifact, "gap layer", "\"gap\"")?;
artifact_contains(artifact, "Vyre test paths", "/matching/vyre/")?;
if artifact.contains("\"oversized\": true") {
return Err(ReleaseGapFindingError::ArtifactMissingEvidence {
evidence: "no oversized gap tests",
});
}
if artifact.contains("\"god_test_candidate\": true") {
return Err(ReleaseGapFindingError::ArtifactMissingEvidence {
evidence: "no god-test gap candidates",
});
}
let file_count = artifact_number_field(artifact, "file_count")?;
let vyre_file_count = artifact_number_field(artifact, "vyre_file_count")?;
let dataflow_consumer_file_count =
artifact_number_field(artifact, "dataflow_consumer_file_count")?;
let vyrec_file_count = artifact_number_field(artifact, "vyrec_file_count")?;
let test_entrypoints = artifact.matches("\"has_test_entrypoint\": true").count() as u64;
let gap_layer_mentions = artifact.matches("\"gap\"").count() as u64;
artifact_at_least("file_count", file_count, 20)?;
artifact_at_least("vyre_file_count", vyre_file_count, 20)?;
artifact_at_least(
"dataflow_consumer_file_count",
dataflow_consumer_file_count,
1,
)?;
artifact_at_least("vyrec_file_count", vyrec_file_count, 1)?;
artifact_at_least("has_test_entrypoint=true", test_entrypoints, 10)?;
artifact_at_least("gap layer mentions", gap_layer_mentions, file_count)?;
Ok(ReleaseGapSuiteArtifactProof {
file_count,
vyre_file_count,
dataflow_consumer_file_count,
vyrec_file_count,
})
}
fn validate_metadata(finding: &ReleaseGapFinding<'_>) -> Result<(), ReleaseGapFindingError> {
for (field, value) in [
("id", finding.id),
("owner", finding.owner),
("feature", finding.feature),
("reproduction_command", finding.reproduction_command),
("evidence_path", finding.evidence_path),
] {
if value.trim().is_empty() {
return Err(ReleaseGapFindingError::EmptyMetadata {
id: finding.id.to_owned(),
field,
});
}
}
Ok(())
}
fn artifact_contains(
artifact: &str,
evidence: &'static str,
needle: &str,
) -> Result<(), ReleaseGapFindingError> {
if artifact.contains(needle) {
Ok(())
} else {
Err(ReleaseGapFindingError::ArtifactMissingEvidence { evidence })
}
}
fn artifact_at_least(
field: &'static str,
observed: u64,
required: u64,
) -> Result<(), ReleaseGapFindingError> {
if observed >= required {
Ok(())
} else {
Err(ReleaseGapFindingError::ArtifactThresholdMiss {
field,
observed,
required,
})
}
}
fn artifact_number_field(
artifact: &str,
field: &'static str,
) -> Result<u64, ReleaseGapFindingError> {
let key = format!("\"{field}\"");
let start = artifact
.find(&key)
.ok_or(ReleaseGapFindingError::ArtifactMissingNumber { field })?;
let after_key = &artifact[start + key.len()..];
let colon = after_key
.find(':')
.ok_or(ReleaseGapFindingError::ArtifactMissingNumber { field })?;
let after_colon = after_key[colon + 1..].trim_start();
let digits = after_colon
.chars()
.take_while(|ch| ch.is_ascii_digit())
.collect::<String>();
if digits.is_empty() {
return Err(ReleaseGapFindingError::ArtifactMissingNumber { field });
}
digits
.parse::<u64>()
.map_err(|_| ReleaseGapFindingError::ArtifactMissingNumber { field })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gap_findings_accept_active_cargo_full_reproductions() {
let proof = validate_release_gap_findings(&[
finding("vyrec-semantic-gap", ReleaseGapStatus::Open),
finding("vyrec-parser-regression", ReleaseGapStatus::Fixed),
])
.expect("Fix: valid gap findings should pass");
assert_eq!(proof.gap_count, 2);
assert_eq!(proof.open_count, 1);
assert_eq!(proof.fixed_count, 1);
}
#[test]
fn gap_findings_reject_raw_cargo_reproduction() {
let mut bad = finding("bad", ReleaseGapStatus::Open);
bad.reproduction_command = "cargo test";
assert_eq!(
validate_release_gap_findings(&[bad]).expect_err("raw cargo should fail"),
ReleaseGapFindingError::ReproductionDoesNotUseCargoFull {
id: "bad".to_owned(),
command: "cargo test".to_owned(),
}
);
}
#[test]
fn gap_findings_require_active_gap_until_completion_audit() {
assert_eq!(
validate_release_gap_findings(&[finding("fixed", ReleaseGapStatus::Fixed)])
.expect_err("no active gap should fail until completion audit"),
ReleaseGapFindingError::NoActiveGap
);
}
#[test]
fn gap_findings_accept_committed_gap_suite_artifact() {
let proof = validate_release_gap_suite_artifact(include_str!(
"../../../../release/evidence/tests/gap-suite-platform.json"
))
.expect("Fix: committed gap suite artifact should pass");
assert!(proof.file_count >= 20);
assert!(proof.vyre_file_count >= 20);
assert!(proof.dataflow_consumer_file_count >= 1);
assert!(proof.vyrec_file_count >= 1);
}
#[test]
fn gap_findings_reject_unowned_or_blocked_gap_suite_artifact() {
let artifact = r#"{
"schema_version": 1,
"suite": "gap",
"file_count": 20,
"vyre_file_count": 20,
"dataflow_consumer_file_count": 0,
"vyrec_file_count": 1,
"blockers": ["missing-dataflow-consumer-gap"],
"files": []
}"#;
assert_eq!(
validate_release_gap_suite_artifact(artifact)
.expect_err("blocked gap suite should fail"),
ReleaseGapFindingError::ArtifactMissingEvidence {
evidence: "zero blockers",
}
);
}
#[test]
fn gap_findings_reject_oversized_gap_suite_artifact() {
let artifact = r#"{
"schema_version": 1,
"suite": "gap",
"file_count": 20,
"vyre_file_count": 20,
"dataflow_consumer_file_count": 1,
"vyrec_file_count": 1,
"blockers": [],
"path": "/matching/vyre/test.rs",
"layers": ["gap"],
"has_test_entrypoint": true,
"oversized": true,
"god_test_candidate": false
}"#;
assert_eq!(
validate_release_gap_suite_artifact(artifact)
.expect_err("oversized gap suite should fail"),
ReleaseGapFindingError::ArtifactMissingEvidence {
evidence: "no oversized gap tests",
}
);
}
fn finding(id: &'static str, status: ReleaseGapStatus) -> ReleaseGapFinding<'static> {
ReleaseGapFinding {
id,
owner: "vyrec",
feature: "clang semantic parity",
reproduction_command: "./cargo_full test -j1 -p vyrec",
evidence_path: "release/gaps/vyrec-clang-parity.md",
status,
}
}
}