Skip to main content

dkp_core/validate/
gate8.rs

1use crate::{
2    okf::parser::parse_okf_dir,
3    pack::loader::Pack,
4    validate::gates::{CheckResult, GateResult, GateStatus},
5};
6
7/// Gate 8: OKF Conformance — frontmatter valid, type field present, bundle.sig present.
8pub fn run(pack: &Pack) -> GateResult {
9    if !pack.has_okf() {
10        return GateResult {
11            gate: 8,
12            status: GateStatus::NotApplicable,
13            checks: vec![CheckResult::skip("okf/ layer (not present)")],
14            message: Some("OKF layer absent; gate 8 not applicable".to_string()),
15        };
16    }
17
18    let mut checks = Vec::new();
19
20    // Walk and parse all OKF concept files
21    match parse_okf_dir(&pack.okf_dir()) {
22        Ok(concepts) => {
23            let n = concepts.len();
24            checks.push(CheckResult::pass(format!("OKF concept files: {n} parsed")));
25
26            // Every concept must have a 'type' key in frontmatter
27            let missing_type: Vec<String> = concepts
28                .iter()
29                .filter(|c| c.frontmatter.get("type").is_none())
30                .map(|c| {
31                    c.path
32                        .file_name()
33                        .unwrap_or_default()
34                        .to_string_lossy()
35                        .into_owned()
36                })
37                .collect();
38
39            if missing_type.is_empty() {
40                checks.push(CheckResult::pass(format!(
41                    "OKF frontmatter: all {n} files have 'type' field"
42                )));
43            } else {
44                checks.push(CheckResult::fail(
45                    "OKF frontmatter: 'type' field required",
46                    format!("missing 'type': {}", missing_type.join(", ")),
47                ));
48            }
49        }
50        Err(e) => {
51            checks.push(CheckResult::fail("OKF concept files parse", e.to_string()));
52        }
53    }
54
55    // bundle.sig presence
56    let bundle_sig = pack.okf_dir().join("bundle.sig");
57    if bundle_sig.exists() {
58        checks.push(CheckResult::pass("bundle.sig present"));
59    } else {
60        checks.push(CheckResult::skip("bundle.sig (not present)"));
61    }
62
63    let failed = checks.iter().any(|c| c.status == GateStatus::Fail);
64    GateResult {
65        gate: 8,
66        status: if failed {
67            GateStatus::Fail
68        } else {
69            GateStatus::Pass
70        },
71        checks,
72        message: None,
73    }
74}