use crate::domain::finding::Finding;
use crate::domain::manifest::{MANIFEST_PATH, Manifest};
use crate::domain::rule_id::RuleId;
use crate::gates::{GateCtx, GateResult, Violation};
pub const CITES: &[RuleId] = &[RuleId::TheManifestStaysReadable];
pub fn run(ctx: &GateCtx, _files: &[String]) -> GateResult {
let violation = |detail: &str| {
vec![Violation::Finding(Finding::on_file(
RuleId::TheManifestStaysReadable,
MANIFEST_PATH,
detail,
))]
};
if ctx.retained([MANIFEST_PATH]).is_empty() {
return Ok(vec![]);
}
let Ok(text) = std::fs::read_to_string(ctx.path(MANIFEST_PATH)) else {
return Ok(violation("invalid manifest shape"));
};
match Manifest::parse(&text) {
Ok(_) => Ok(vec![]),
Err(error) => Ok(violation(&format!("invalid manifest shape: {error}"))),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn run_with_manifest(json: Option<&str>) -> Vec<String> {
let dir = tempfile::tempdir().unwrap();
if let Some(json) = json {
let instance = dir.path().join(".spec-driven-docs");
std::fs::create_dir_all(&instance).unwrap();
std::fs::write(instance.join("manifest.json"), json).unwrap();
}
let ctx = GateCtx::new(dir.path().to_str().unwrap());
run(&ctx, &[])
.unwrap()
.iter()
.map(ToString::to_string)
.collect()
}
const VALID: &str = r#"{
"schema_version": 3,
"canon_version": "0.2.0",
"canon_source": "https://github.com/gubasso/spec-driven-docs",
"profile": "knowledge-base",
"docs_root": "_docs",
"installed_at": "2026-08-25T00:00:00Z",
"managed_files": [
{"source": ".markdownlint/spec.markdownlint-cli2.jsonc",
"destination": ".spec-driven-docs/markdownlint/spec.markdownlint-cli2.jsonc",
"sha256": "dc17d596ae2c196cc01b439c291416f91198cc274e2376fd01a4d614c1ff60ad"}
],
"adopted_files": [],
"integration_blocks": []
}"#;
#[test]
fn accepts_a_current_schema_manifest() {
assert!(run_with_manifest(Some(VALID)).is_empty());
}
#[test]
fn rejects_a_missing_or_older_manifest() {
let missing = run_with_manifest(None);
assert_eq!(missing.len(), 1);
assert!(missing[0].contains("instance:the-manifest-stays-readable"));
let older = VALID.replace("\"schema_version\": 3", "\"schema_version\": 1");
let out = run_with_manifest(Some(&older));
assert_eq!(out.len(), 1);
assert!(out[0].contains("invalid manifest shape: manifest schema_version 1"));
}
}