use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use serde::Deserialize;
use sha2::{Digest, Sha256};
const MAX_WAIVER_SECS: u64 = 90 * 24 * 60 * 60;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Gate7Finding {
pub op_id: String,
pub missing: Vec<CoverageGap>,
pub fix: String,
}
impl Gate7Finding {
fn meta(kind: &str, fix: impl Into<String>) -> Self {
Self {
op_id: kind.to_string(),
missing: Vec::new(),
fix: fix.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CoverageGap {
Golden,
Kat,
Adversarial,
Proptest,
}
impl CoverageGap {
pub const fn column_name(self) -> &'static str {
match self {
Self::Golden => "golden",
Self::Kat => "kat",
Self::Adversarial => "adversarial",
Self::Proptest => "proptest",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct SourceHash {
path: PathBuf,
sha256: String,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct WaiverFile {
#[serde(default)]
waivers: Vec<CoverageWaiver>,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct CoverageWaiver {
op_id: String,
reason: String,
approver: String,
expires_epoch_secs: u64,
justification_url: String,
}
#[inline]
pub fn enforce_gate_7_from_report(workspace_root: &Path) -> Result<(), Vec<Gate7Finding>> {
let report_path = workspace_root
.join("docs")
.join("generated")
.join("coverage-report.md");
let contents = match fs::read_to_string(&report_path) {
Ok(contents) => contents,
Err(_) => return Err(missing_report_findings(workspace_root, &report_path)),
};
if let Err(finding) = validate_report_hashes(workspace_root, &contents) {
return Err(vec![finding]);
}
let waivers = match load_coverage_waivers(workspace_root) {
Ok(waivers) => waivers,
Err(finding) => return Err(vec![finding]),
};
let mut findings = Vec::new();
let mut present_ops = BTreeSet::new();
for line in contents.lines() {
let Some((op_id, gaps)) = parse_coverage_row(line) else {
continue;
};
present_ops.insert(op_id.clone());
if gaps.is_empty() || waivers.contains(&op_id) {
continue;
}
findings.push(Gate7Finding {
fix: format!(
"Op `{}` is missing: {}. Fix: add the required artifacts inside the op source \
(GOLDEN / KAT / ADVERSARIAL consts plus a proptest invariant) or add a typed, \
unexpired waiver in conform/waivers/op_coverage_waivers.toml.",
op_id,
gaps.iter()
.map(|gap| gap.column_name())
.collect::<Vec<_>>()
.join(", ")
),
op_id,
missing: gaps,
});
}
for spec in crate::spec::op_registry::all_specs() {
if present_ops.contains(spec.id) || waivers.contains(spec.id) {
continue;
}
findings.push(Gate7Finding {
op_id: spec.id.to_string(),
missing: vec![
CoverageGap::Golden,
CoverageGap::Kat,
CoverageGap::Adversarial,
CoverageGap::Proptest,
],
fix: format!(
"Coverage report omits registered op `{}`. Fix: rebuild conform so \
docs/generated/coverage-report.md contains one row for every registry op, then \
add any missing coverage artifacts.",
spec.id
),
});
}
if findings.is_empty() {
Ok(())
} else {
Err(findings)
}
}
#[inline]
pub fn load_coverage_waivers(workspace_root: &Path) -> Result<BTreeSet<String>, Gate7Finding> {
let path = workspace_root
.join("conform")
.join("waivers")
.join("op_coverage_waivers.toml");
let contents = match fs::read_to_string(&path) {
Ok(contents) => contents,
Err(_) => return Ok(BTreeSet::new()),
};
let parsed: WaiverFile = toml::from_str(&contents).map_err(|error| {
Gate7Finding::meta(
"__waivers__",
format!(
"Malformed coverage waiver file {}: {error}. Fix: use typed [[waivers]] entries \
with op_id, reason, approver, expires_epoch_secs, and justification_url.",
path.display()
),
)
})?;
let now = current_epoch_secs().map_err(|error| {
Gate7Finding::meta(
"__waivers__",
format!("Cannot validate coverage waiver age: {error}. Fix: repair system clock."),
)
})?;
let mut out = BTreeSet::new();
for waiver in parsed.waivers {
validate_waiver(&path, &waiver, now)?;
if !waiver.op_id.starts_with("__example_") {
out.insert(waiver.op_id);
}
}
Ok(out)
}
fn validate_waiver(path: &Path, waiver: &CoverageWaiver, now: u64) -> Result<(), Gate7Finding> {
let fail = |message: String| Gate7Finding::meta("__waivers__", message);
if waiver.op_id.trim().is_empty()
|| waiver.reason.trim().is_empty()
|| waiver.approver.trim().is_empty()
|| waiver.justification_url.trim().is_empty()
{
return Err(fail(format!(
"Malformed coverage waiver in {} for `{}`. Fix: op_id, reason, approver, and \
justification_url must all be non-empty.",
path.display(),
waiver.op_id
)));
}
if !(waiver.justification_url.starts_with("https://")
|| waiver.justification_url.starts_with("http://"))
{
return Err(fail(format!(
"Malformed coverage waiver for `{}` in {}. Fix: justification_url must be an \
absolute http(s) URL.",
waiver.op_id,
path.display()
)));
}
if waiver.expires_epoch_secs <= now {
return Err(fail(format!(
"Expired coverage waiver for `{}` in {}. Fix: remove the waiver or renew it after \
adding a current approval.",
waiver.op_id,
path.display()
)));
}
if waiver.expires_epoch_secs.saturating_sub(now) > MAX_WAIVER_SECS {
return Err(fail(format!(
"Coverage waiver for `{}` in {} lives longer than 90 days. Fix: set \
expires_epoch_secs to a timestamp no more than 90 days from now.",
waiver.op_id,
path.display()
)));
}
Ok(())
}
fn missing_report_findings(workspace_root: &Path, report_path: &Path) -> Vec<Gate7Finding> {
let specs = crate::spec::op_registry::all_specs();
if specs.is_empty() {
return vec![Gate7Finding::meta(
"__registry__",
format!(
"Coverage report not found at {} and registry is empty. Fix: rebuild conform so \
`cd conform/codegen && cargo run --offline -- regenerate` rewrites docs/generated/coverage-report.md.",
report_path.display()
),
)];
}
specs
.into_iter()
.map(|spec| Gate7Finding {
op_id: spec.id.to_string(),
missing: Vec::new(),
fix: format!(
"Coverage report not found at {}. Fix: run `cd conform/codegen && cargo run \
--offline -- regenerate` from {} so docs/generated/coverage-report.md is current.",
report_path.display(),
workspace_root.display()
),
})
.collect()
}
fn validate_report_hashes(workspace_root: &Path, contents: &str) -> Result<(), Gate7Finding> {
let hashes = parse_source_hashes(contents).map_err(|error| {
Gate7Finding::meta(
"__report__",
format!(
"Coverage report is missing or has a malformed source-hash header: {error}. Fix: \
run `cd conform/codegen && cargo run --offline -- regenerate` so docs/generated/coverage-report.md is current."
),
)
})?;
for expected in hashes {
let path = workspace_root.join(&expected.path);
let bytes = fs::read(&path).map_err(|error| {
Gate7Finding::meta(
"__report__",
format!(
"Coverage report references unreadable source {}: {error}. Fix: restore the \
source file or regenerate the coverage report.",
expected.path.display()
),
)
})?;
let actual = sha256_hex(&bytes);
if actual != expected.sha256 {
return Err(Gate7Finding::meta(
"__report__",
format!(
"Coverage report is stale for {}: expected {}, current {}. Fix: rebuild \
conform so the report header records the current source hash.",
expected.path.display(),
expected.sha256,
actual
),
));
}
}
Ok(())
}
fn parse_source_hashes(contents: &str) -> Result<Vec<SourceHash>, String> {
let mut in_header = false;
let mut saw_begin = false;
let mut saw_end = false;
let mut hashes = Vec::new();
for line in contents.lines() {
let trimmed = line.trim();
if trimmed == "<!-- gate7-source-hashes:begin -->" {
saw_begin = true;
in_header = true;
continue;
}
if trimmed == "<!-- gate7-source-hashes:end -->" {
saw_end = true;
in_header = false;
continue;
}
if !in_header {
continue;
}
let Some(rest) = trimmed.strip_prefix("<!-- gate7-source-sha256 ") else {
return Err(format!("unexpected header line `{trimmed}`"));
};
let Some(rest) = rest.strip_suffix(" -->") else {
return Err(format!("unterminated header line `{trimmed}`"));
};
let mut parts = rest.split_whitespace();
let path = parts.next().ok_or("missing source path")?;
let sha256 = parts.next().ok_or("missing sha256")?;
if parts.next().is_some()
|| sha256.len() != 64
|| !sha256.chars().all(|c| c.is_ascii_hexdigit())
{
return Err(format!("invalid source hash line `{trimmed}`"));
}
hashes.push(SourceHash {
path: PathBuf::from(path),
sha256: sha256.to_ascii_lowercase(),
});
}
if !saw_begin || !saw_end {
return Err("missing gate7-source-hashes begin/end markers".to_string());
}
if hashes.is_empty() {
return Err("source-hash header is empty".to_string());
}
Ok(hashes)
}
fn parse_coverage_row(line: &str) -> Option<(String, Vec<CoverageGap>)> {
let trimmed = line.trim();
if !trimmed.starts_with('|') {
return None;
}
let columns: Vec<&str> = trimmed
.split('|')
.map(str::trim)
.filter(|col| !col.is_empty())
.collect();
if columns.len() < 5 {
return None;
}
let op_id = columns[0].to_string();
if op_id == "id" || op_id.starts_with("---") {
return None;
}
let status_column = columns.last()?;
let mut gaps = Vec::new();
for gap in [
CoverageGap::Golden,
CoverageGap::Kat,
CoverageGap::Adversarial,
CoverageGap::Proptest,
] {
if status_column.contains(&format!("{}: no", gap.column_name())) {
gaps.push(gap);
}
}
Some((op_id, gaps))
}
fn current_epoch_secs() -> Result<u64, String> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_secs())
.map_err(|error| error.to_string())
}
fn sha256_hex(bytes: &[u8]) -> String {
vyre_reference::hash::hex::bytes_to_hex(&Sha256::digest(bytes))
}
pub struct Gate7CoverageEnforcer;
impl crate::enforce::EnforceGate for Gate7CoverageEnforcer {
fn id(&self) -> &'static str {
"gate_7_coverage"
}
fn name(&self) -> &'static str {
"gate_7_coverage"
}
fn run(&self, ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<crate::enforce::Finding> {
match enforce_gate_7_from_report(ctx.workspace_root) {
Ok(()) => Vec::new(),
Err(findings) => crate::enforce::finding_result(
self.id(),
findings.into_iter().map(|finding| finding.fix).collect(),
),
}
}
}
pub const REGISTERED: Gate7CoverageEnforcer = Gate7CoverageEnforcer;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_row_flags_all_four_missing_artifacts() {
let line = "| primitive.encoding.base64 | L2 | A | `(Bytes) -> Bytes` | golden: no; kat: no; adversarial: no; proptest: no |";
let (id, gaps) = parse_coverage_row(line).expect("row must parse");
assert_eq!(id, "primitive.encoding.base64");
assert_eq!(
gaps,
vec![
CoverageGap::Golden,
CoverageGap::Kat,
CoverageGap::Adversarial,
CoverageGap::Proptest,
]
);
}
#[test]
fn parse_row_ignores_fully_covered_op() {
let line = "| primitive.bitwise.xor | L1 | A | `(U32, U32) -> U32` | golden: yes; kat: yes; adversarial: yes; proptest: yes |";
let (_, gaps) = parse_coverage_row(line).expect("row must parse");
assert!(gaps.is_empty(), "fully covered op must not report any gaps");
}
#[test]
fn parse_source_hashes_requires_header() {
let err = parse_source_hashes("# Generated coverage report").unwrap_err();
assert!(err.contains("begin/end"), "{err}");
}
#[test]
fn empty_report_rows_leave_present_set_empty() {
let mut present_ops = BTreeSet::new();
for line in [
"<!-- gate7-source-hashes:begin -->",
"<!-- gate7-source-sha256 conform/src/lib.rs 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -->",
"<!-- gate7-source-hashes:end -->",
"| id | layer | category | signature | status |",
"| --- | --- | --- | --- | --- |",
] {
if let Some((op_id, _)) = parse_coverage_row(line) {
present_ops.insert(op_id);
}
}
assert!(
present_ops.is_empty(),
"header-only coverage reports must not create fake op rows"
);
}
}