use std::collections::HashSet;
use crate::ir_nodes::IRProgram;
use axon_frontend::compliance::{peel_channel_payload, peel_type_constructors};
pub fn compliance_coverage_holds(program: &IRProgram) -> bool {
let kappa_of_type = |type_ref: &str| -> HashSet<&str> {
let base = peel_type_constructors(type_ref);
program
.types
.iter()
.find(|t| t.name == base)
.map(|t| t.compliance.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
};
let shield_covers = |required: &HashSet<&str>, shield_ref: &str| -> bool {
program
.shields
.iter()
.find(|s| s.name == shield_ref)
.is_some_and(|s| {
let provided: HashSet<&str> =
s.compliance.iter().map(|x| x.as_str()).collect();
required.is_subset(&provided)
})
};
let mut any_regulated = false;
for e in &program.endpoints {
if e.execute_flow.is_empty() {
continue;
}
let mut required = kappa_of_type(&e.body_type);
required.extend(kappa_of_type(&e.output_type));
if required.is_empty() {
continue;
}
any_regulated = true;
if !shield_covers(&required, &e.shield_ref) {
return false;
}
}
for c in &program.channels {
let required = kappa_of_type(peel_channel_payload(&c.message));
if required.is_empty() {
continue;
}
any_regulated = true;
if !shield_covers(&required, &c.shield_ref) {
return false;
}
}
any_regulated
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir_generator::IRGenerator;
use crate::lexer::Lexer;
use crate::parser::Parser;
fn compile(source: &str) -> IRProgram {
let tokens = Lexer::new(source, "t").tokenize().unwrap();
let program = Parser::new(tokens).parse().unwrap();
IRGenerator::new().generate(&program)
}
#[test]
fn an_annotation_nothing_carries_is_a_label_not_coverage() {
let ir = compile("type Phi compliance [HIPAA] { x: String }");
assert!(
!compliance_coverage_holds(&ir),
"a κ-annotated type that no endpoint or channel carries exercises \
no coverage rule — scoring it was the presence-not-coverage defect"
);
}
#[test]
fn an_uncovered_regulated_channel_denies_the_feature() {
let ir = compile(
r#"
type Phi compliance [HIPAA] { x: String }
shield Sieve { scan: [pii_leak] }
channel PhiFeed { message: Phi shield: Sieve }
"#,
);
assert!(!compliance_coverage_holds(&ir));
}
#[test]
fn a_covered_regulated_channel_grants_the_feature() {
let ir = compile(
r#"
type Phi compliance [HIPAA] { x: String }
shield Sieve { scan: [pii_leak] compliance: [HIPAA, SOC2] }
channel PhiFeed { message: Channel<Phi> shield: Sieve }
"#,
);
assert!(
compliance_coverage_holds(&ir),
"`Channel<Phi>` carries Phi's κ — the peel must see through the wrapper"
);
}
#[test]
fn one_uncovered_boundary_poisons_an_otherwise_covered_program() {
let ir = compile(
r#"
type Phi compliance [HIPAA] { x: String }
type Card compliance [PCI_DSS] { x: String }
shield Sieve { scan: [pii_leak] compliance: [HIPAA] }
channel PhiFeed { message: Phi shield: Sieve }
channel CardFeed { message: Card shield: Sieve }
"#,
);
assert!(
!compliance_coverage_holds(&ir),
"coverage is a universal claim — one uncovered boundary and the \
dossier may not assert the risk is mitigated"
);
}
#[test]
fn an_unknown_or_absent_shield_fails_closed() {
let no_shield = compile(
r#"
type Phi compliance [HIPAA] { x: String }
channel PhiFeed { message: Phi }
"#,
);
assert!(!compliance_coverage_holds(&no_shield), "no shield at all");
let ghost = compile(
r#"
type Phi compliance [HIPAA] { x: String }
channel PhiFeed { message: Phi shield: Ghost }
"#,
);
assert!(
!compliance_coverage_holds(&ghost),
"a shield the IR cannot resolve covers nothing"
);
}
#[test]
fn endpoints_participate_with_t957_gating() {
let covered = compile(
r#"
type Phi compliance [HIPAA] { x: String }
flow F(r: Phi) -> Phi { step S { ask: "x" output: Phi } }
shield Sieve { scan: [pii_leak] compliance: [HIPAA] }
axonendpoint E {
method: POST path: "/p" body: Phi execute: F output: Phi
shield: Sieve
}
"#,
);
assert!(compliance_coverage_holds(&covered));
let inert = compile(
r#"
type Phi compliance [HIPAA] { x: String }
shield Sieve { scan: [pii_leak] compliance: [HIPAA] }
axonendpoint E {
method: POST path: "/p" body: Phi output: Phi
shield: Sieve
}
"#,
);
assert!(!compliance_coverage_holds(&inert));
}
}