#![allow(dead_code)]
use std::collections::{BTreeMap, HashMap, HashSet};
use serde::Serialize;
use serde_json::{Map, Value};
use crate::ir_nodes::IRProgram;
use super::frameworks::{Control, EvidenceKind, FrameworkId, controls_for};
#[derive(Debug, Clone, Serialize)]
pub struct ControlAssessment {
pub control_id: String,
pub title: String,
pub axon_primitive: String,
pub evidence_kind: String,
pub evidence_locator: String,
pub status: String,
pub rationale: String,
}
#[derive(Debug, Clone)]
pub struct GapAnalysis {
pub framework: String,
pub total_controls: usize,
pub ready: usize,
pub pending_code: usize,
pub pending_external: usize,
pub assessments: Vec<ControlAssessment>,
pub missing_features: Vec<String>,
pub unbacked_runtime_claims: Vec<String>,
pub declared_but_unenforced: Vec<String>,
}
impl GapAnalysis {
pub fn readiness_percent(&self) -> f64 {
if self.total_controls == 0 {
return 100.0;
}
100.0 * self.ready as f64 / self.total_controls as f64
}
pub fn to_value(&self) -> Value {
let mut m = Map::new();
m.insert("schema".into(), "axon.esk.audit_gap_analysis.v1".into());
m.insert("framework".into(), self.framework.clone().into());
m.insert("total_controls".into(), (self.total_controls as i64).into());
m.insert("ready".into(), (self.ready as i64).into());
m.insert("pending_code".into(), (self.pending_code as i64).into());
m.insert("pending_external".into(), (self.pending_external as i64).into());
let pct = (self.readiness_percent() * 100.0).round() / 100.0;
m.insert("readiness_percent".into(), Value::from(pct));
m.insert(
"missing_features".into(),
Value::Array(
self.missing_features.iter().cloned().map(Value::String).collect(),
),
);
m.insert(
"unbacked_runtime_claims".into(),
Value::Array(
self.unbacked_runtime_claims.iter().cloned().map(Value::String).collect(),
),
);
m.insert(
"declared_but_unenforced".into(),
Value::Array(
self.declared_but_unenforced.iter().cloned().map(Value::String).collect(),
),
);
m.insert(
"assessments".into(),
Value::Array(
self.assessments
.iter()
.map(|a| {
let mut am = Map::new();
am.insert("control_id".into(), a.control_id.clone().into());
am.insert("title".into(), a.title.clone().into());
am.insert("axon_primitive".into(), a.axon_primitive.clone().into());
am.insert("evidence_kind".into(), a.evidence_kind.clone().into());
am.insert("evidence_locator".into(), a.evidence_locator.clone().into());
am.insert("status".into(), a.status.clone().into());
am.insert("rationale".into(), a.rationale.clone().into());
Value::Object(am)
})
.collect(),
),
);
Value::Object(m)
}
}
fn declared_features(program: &IRProgram) -> HashSet<String> {
let mut features: HashSet<String> = HashSet::new();
if !program.shields.is_empty() { features.insert("has_shield".into()); }
if !program.resources.is_empty() { features.insert("has_resource".into()); }
if !program.manifests.is_empty() { features.insert("has_manifest".into()); }
if !program.observations.is_empty() { features.insert("has_observe".into()); }
if !program.immunes.is_empty() { features.insert("has_immune".into()); }
if !program.reflexes.is_empty() { features.insert("has_reflex".into()); }
if !program.heals.is_empty() { features.insert("has_heal".into()); }
if !program.reconciles.is_empty() { features.insert("has_reconcile".into()); }
if !program.leases.is_empty() { features.insert("has_lease".into()); }
if !program.ensembles.is_empty() { features.insert("has_ensemble".into()); }
if !program.topologies.is_empty() { features.insert("has_topology".into()); }
if !program.endpoints.is_empty() { features.insert("has_endpoint".into()); }
if super::coverage::compliance_coverage_holds(program) {
features.insert("compliance_coverage_holds".into());
}
features
}
fn enforced_features(declared: &HashSet<String>) -> HashSet<String> {
declared
.iter()
.filter(|f| super::runtime_wiring::feature_is_enforced(f))
.cloned()
.collect()
}
fn feature_requirements() -> HashMap<&'static str, HashSet<&'static str>> {
let mut m: HashMap<&'static str, HashSet<&'static str>> = HashMap::new();
m.insert("CC3.2", HashSet::from(["has_immune"]));
m.insert("CC3.3", HashSet::from(["has_reflex"]));
m.insert("CC6.3", HashSet::from(["has_lease"]));
m.insert("CC6.6", HashSet::from(["has_shield", "has_endpoint"]));
m.insert("CC6.8", HashSet::new());
m.insert("CC7.1", HashSet::from(["has_immune"]));
m.insert("CC7.2", HashSet::from(["has_immune"]));
m.insert("CC7.3", HashSet::from(["has_immune"]));
m.insert("CC7.4", HashSet::from(["has_reflex", "has_heal"]));
m.insert("CC7.5", HashSet::from(["has_reconcile"]));
m.insert("C1.1", HashSet::from(["compliance_coverage_holds"]));
m.insert("PI1.4", HashSet::from(["has_ensemble"]));
m.insert("P1.1", HashSet::from(["compliance_coverage_holds"]));
m.insert("P6.1", HashSet::from(["has_shield", "compliance_coverage_holds"]));
m.insert("A.5.2", HashSet::from(["has_heal"]));
m.insert("A.5.7", HashSet::from(["has_immune"]));
m.insert("A.5.23", HashSet::new());
m.insert("A.5.24", HashSet::from(["has_immune", "has_reflex", "has_heal"]));
m.insert("A.5.30", HashSet::from(["has_reconcile"]));
m.insert("A.5.34", HashSet::from(["compliance_coverage_holds"]));
m.insert("A.8.2", HashSet::from(["has_lease"]));
m.insert("A.8.7", HashSet::from(["has_immune", "has_reflex"]));
m.insert("A.8.8", HashSet::from(["has_heal"]));
m.insert("A.8.13", HashSet::from(["has_resource"]));
m
}
const PENDING_KEYWORD: &str = "PENDING";
fn is_external_kind(kind: EvidenceKind) -> bool {
matches!(
kind,
EvidenceKind::ExternalOperational | EvidenceKind::ManualPolicy
)
}
fn assess_control(
control: &Control,
declared: &HashSet<String>,
enforced: &HashSet<String>,
) -> ControlAssessment {
let locator = control.evidence_locator;
let is_pending = locator.contains(PENDING_KEYWORD);
let reqs = feature_requirements();
let required: HashSet<&str> = reqs.get(control.control_id).cloned().unwrap_or_default();
let mut not_declared: Vec<&str> = required
.iter()
.filter(|f| !declared.contains(**f))
.copied()
.collect();
let mut declared_but_unenforced: Vec<&str> = required
.iter()
.filter(|f| declared.contains(**f) && !enforced.contains(**f))
.copied()
.collect();
not_declared.sort();
declared_but_unenforced.sort();
let wiring = super::runtime_wiring::wiring_or_absent(locator);
let runtime_claim_unbacked =
control.evidence_kind == EvidenceKind::RuntimeInvariant && !wiring.is_enforced();
let (status, rationale) = if is_pending && is_external_kind(control.evidence_kind) {
(
"pending_external",
format!("requires external engagement (accredited lab / CPA) — {locator}"),
)
} else if is_pending {
(
"pending_code",
format!("evidence artifact not yet produced — {locator}"),
)
} else if runtime_claim_unbacked {
("pending_code", wiring.rationale(locator))
} else if !declared_but_unenforced.is_empty() {
(
"pending_code",
format!(
"program declares {} but AXON does not enforce {} at runtime — \
the primitive has no dispatch path; declaring it is not evidence",
declared_but_unenforced.join(", "),
if declared_but_unenforced.len() == 1 { "it" } else { "them" },
),
)
} else if !not_declared.is_empty() {
(
"pending_code",
format!(
"program does not declare required primitive(s): {}",
not_declared.join(", ")
),
)
} else if control.evidence_kind == EvidenceKind::ExternalOperational {
("ready", format!("operational artifact: {locator}"))
} else if control.evidence_kind == EvidenceKind::RuntimeInvariant {
("ready", wiring.rationale(locator))
} else {
("ready", format!("enforced by {}", control.axon_primitive))
};
ControlAssessment {
control_id: control.control_id.into(),
title: control.title.into(),
axon_primitive: control.axon_primitive.into(),
evidence_kind: control.evidence_kind.as_str().into(),
evidence_locator: locator.into(),
status: status.into(),
rationale,
}
}
pub fn analyze_gaps(program: &IRProgram, framework: FrameworkId) -> GapAnalysis {
let declared = declared_features(program);
let enforced = enforced_features(&declared);
let controls = controls_for(framework);
let mut analysis = GapAnalysis {
framework: framework.as_str().into(),
total_controls: controls.len(),
ready: 0,
pending_code: 0,
pending_external: 0,
assessments: Vec::new(),
missing_features: Vec::new(),
unbacked_runtime_claims: Vec::new(),
declared_but_unenforced: Vec::new(),
};
let reqs = feature_requirements();
let mut declared_unenforced: Vec<String> = declared
.iter()
.filter(|f| !enforced.contains(*f))
.cloned()
.collect();
declared_unenforced.sort();
analysis.declared_but_unenforced = declared_unenforced;
for c in &controls {
if c.evidence_kind == EvidenceKind::RuntimeInvariant
&& !super::runtime_wiring::wiring_or_absent(c.evidence_locator).is_enforced()
{
analysis.unbacked_runtime_claims.push(format!(
"{} ({})",
c.control_id, c.evidence_locator
));
}
let a = assess_control(c, &declared, &enforced);
match a.status.as_str() {
"ready" => analysis.ready += 1,
"pending_code" => {
analysis.pending_code += 1;
if let Some(req) = reqs.get(c.control_id) {
for feat in req {
if !enforced.contains(*feat)
&& !analysis.missing_features.contains(&feat.to_string())
{
analysis.missing_features.push(feat.to_string());
}
}
}
}
"pending_external" => analysis.pending_external += 1,
_ => {}
}
analysis.assessments.push(a);
}
analysis
}
pub fn analyze_all(program: &IRProgram) -> BTreeMap<String, GapAnalysis> {
let mut m = BTreeMap::new();
for f in super::frameworks::all_frameworks() {
m.insert(f.as_str().into(), analyze_gaps(program, f));
}
m
}
#[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 empty_program_counts_sum_to_total_controls() {
let ir = compile("type X { field: String }");
let gap = analyze_gaps(&ir, FrameworkId::Soc2TypeII);
assert_eq!(
gap.ready + gap.pending_code + gap.pending_external,
gap.total_controls
);
assert!(gap.pending_code > 0);
}
#[test]
fn the_cognitive_io_stack_buys_credit_for_exactly_what_runs() {
let stack = r#"
fabric Vpc { provider: aws }
resource Db { kind: postgres endpoint: db.main within: Vpc lifetime: affine capacity: 20 }
axonstore Users { backend: postgresql resource: Db }
manifest M { resources: [Db] fabric: Vpc compliance: [HIPAA] }
observe O from M { sources: [prom] quorum: 1 }
observe O2 from M { sources: [cw] quorum: 1 }
reconcile Rec { observe: O }
lease L { resource: Db duration: 30m }
ensemble En { observations: [O, O2] quorum: 2 }
immune I { watch: [O] scope: tenant }
reflex Rf { trigger: I on_level: doubt action: quarantine scope: tenant }
heal H { source: I scope: tenant }
"#;
let base = "type R compliance [HIPAA] { x: String }
flow F(r: R) -> R { step S { ask: \"x\" output: R } }
";
let without = analyze_gaps(&compile(base), FrameworkId::Soc2TypeII);
let with = analyze_gaps(&compile(&format!("{base}{stack}")), FrameworkId::Soc2TypeII);
assert!(
with.ready > without.ready,
"the six Cognitive-I/O primitives the supervisor wired (observe/ensemble/immune/reflex/heal/ reconcile) are driven by the supervisor through the real deploy path. Declaring them MUST now raise the score — an engine that can only say no is honest and useless. ready went {} -> {}",
without.ready,
with.ready
);
assert!(
with.declared_but_unenforced.is_empty(),
"as of v2.67.0 every primitive in this stack is on a production path; the analysis must report NOTHING as declared-but-unenforced. Got: {:?}",
with.declared_but_unenforced
);
for f in ["has_lease", "has_resource", "has_immune", "has_heal", "has_reconcile", "has_ensemble", "has_observe"] {
assert!(
!with.declared_but_unenforced.contains(&f.to_string()),
"`{f}` runs on a production path (supervisor / resource+lease) — it must NOT be reported as unenforced"
);
}
for a in &with.assessments {
if a.evidence_kind == "runtime_invariant"
&& !crate::esk::audit_engine::runtime_wiring::wiring_or_absent(&a.evidence_locator)
.is_enforced()
{
assert_ne!(
a.status, "ready",
"control {} is `ready` on an unbacked runtime invariant (`{}`)",
a.control_id, a.evidence_locator
);
}
}
}
#[test]
fn c11_requires_coverage_not_presence() {
let status_of = |ir: &IRProgram| {
analyze_gaps(ir, FrameworkId::Soc2TypeII)
.assessments
.iter()
.find(|a| a.control_id == "C1.1")
.expect("SOC2 catalog carries C1.1")
.status
.clone()
};
let label_only = compile("type Phi compliance [HIPAA] { x: String }");
assert_ne!(
status_of(&label_only),
"ready",
"a κ label that crosses no boundary satisfies no confidentiality control"
);
let covered = compile(
r#"
type Phi compliance [HIPAA] { x: String }
shield Gate { scan: [pii_leak] compliance: [HIPAA] }
channel PhiFeed { message: Phi shield: Gate }
"#,
);
assert_eq!(
status_of(&covered),
"ready",
"coverage holding at every regulated boundary is what `ready` means here"
);
}
#[test]
fn a_wired_runtime_invariant_can_still_be_ready() {
let ir = compile(
r#"
type R { x: String }
flow F(r: R) -> R { step S { ask: "x" output: R } }
"#,
);
let gap = analyze_gaps(&ir, FrameworkId::Soc2TypeII);
let wired_ready = gap.assessments.iter().any(|a| {
a.evidence_kind == "runtime_invariant"
&& a.status == "ready"
&& crate::esk::audit_engine::runtime_wiring::wiring_or_absent(&a.evidence_locator)
.is_enforced()
});
assert!(
wired_ready,
"no wired RuntimeInvariant reached `ready` — the ProvenanceChain / HmacSigner \
audit chain IS on the production path and must still count"
);
}
#[test]
fn fips_has_pending_external_for_lab_engagement() {
let ir = compile("type T { x: String }");
let gap = analyze_gaps(&ir, FrameworkId::Fips140_3);
assert!(
gap.pending_external > 0,
"FIPS must carry ≥1 pending_external entry"
);
}
#[test]
fn analyze_all_covers_every_framework() {
let ir = compile("type T { x: String }");
let all = analyze_all(&ir);
assert_eq!(all.len(), 4);
for fw in super::super::frameworks::all_frameworks() {
assert!(all.contains_key(fw.as_str()));
}
}
#[test]
fn readiness_percent_sane_bounds() {
let ir = compile("type T { x: String }");
let gap = analyze_gaps(&ir, FrameworkId::Iso27001);
let pct = gap.readiness_percent();
assert!((0.0..=100.0).contains(&pct));
}
#[test]
fn gap_analysis_deterministic() {
let ir = compile(r#"
type T compliance [GDPR] { x: String }
resource R { kind: postgres lifetime: linear }
"#);
let a = analyze_gaps(&ir, FrameworkId::Iso27001).to_value();
let b = analyze_gaps(&ir, FrameworkId::Iso27001).to_value();
assert_eq!(
serde_json::to_string(&a).unwrap(),
serde_json::to_string(&b).unwrap()
);
}
}