use crate::git_ops::CommitInfo;
use crate::types::Finding;
use globset::{Glob, GlobBuilder};
pub const FINDING_CLASS: &str = "out-of-contract-write";
pub const PRIMARY_CHECKOUT_SUBJECT: &str = "primary-checkout";
pub fn grantable_touch_path<'a>(
finding: &'a Finding,
current_touch_set: &[String],
) -> Option<&'a str> {
if finding.class != FINDING_CLASS || finding.subject == PRIMARY_CHECKOUT_SUBJECT {
return None;
}
matches!(
touch_set_includes(current_touch_set, &finding.subject),
Ok(false)
)
.then_some(finding.subject.as_str())
}
const ENGINE_COMMIT_PREFIX: &str = "[kranz]";
const ENGINE_META_TEMPLATES: &[&str] = &[
"[kranz] approved plan for ",
"[kranz] revised plan for ",
"[kranz] mission report",
];
pub fn cargo_test_has_anti_vacuity(command: &str) -> bool {
let lower = command.to_ascii_lowercase();
if !lower.contains("cargo test") && !lower.contains("cargo\ttest") {
return true;
}
command.contains("[1-9]") || command.contains("ok\\. [1-9]") || command.contains("ok. [1-9]")
}
pub fn checkpoint_commit_message(feature_id: &str) -> String {
format!("[{feature_id}] checkpoint (engine commit)")
}
pub fn parallel_checkpoint_commit_message(feature_id: &str) -> String {
format!("[{feature_id}] parallel worktree checkpoint (engine commit)")
}
pub fn pool_checkpoint_commit_message(feature_id: &str, index: usize) -> String {
format!("[{feature_id}] dispatch pool candidate {index} checkpoint (engine commit)")
}
pub fn is_meta_commit(subject: &str) -> bool {
if !subject.starts_with(ENGINE_COMMIT_PREFIX) {
return false;
}
ENGINE_META_TEMPLATES
.iter()
.any(|tmpl| subject == *tmpl || subject.starts_with(tmpl))
}
pub fn is_meta_commit_with_paths(
subject: &str,
mission_id: &str,
changed_paths: &[String],
) -> bool {
is_meta_commit(subject)
&& changed_paths
.iter()
.all(|path| is_mission_record_path(mission_id, path))
}
const MISSION_RECORD_FILES: &[&str] = &[
"plan.json", "plan.md", "revised-plan.md", "research.md", "report.md", ];
pub fn is_mission_record_path(mission_id: &str, path: &str) -> bool {
if path == ".kranz/missions/index.md" {
return true;
}
match path.strip_prefix(&format!(".kranz/missions/{mission_id}/")) {
Some(record) => MISSION_RECORD_FILES.contains(&record),
None => false,
}
}
pub fn meta_paths(mission_id: &str) -> Vec<String> {
vec![
format!(".kranz/missions/{mission_id}/plan.json"),
format!(".kranz/missions/{mission_id}/plan.md"),
format!(".kranz/missions/{mission_id}/report.md"),
".kranz/missions/index.md".to_string(),
]
}
pub fn is_meta_path(mission_id: &str, path: &str) -> bool {
meta_paths(mission_id).iter().any(|p| p == path)
}
struct TouchPattern {
glob: globset::GlobMatcher,
negate: bool,
}
fn compile_touch_set(patterns: &[String]) -> Result<Vec<TouchPattern>, globset::Error> {
patterns
.iter()
.map(|raw| {
let (negate, pat) = match raw.strip_prefix('!') {
Some(rest) => (true, rest),
None => (false, raw.as_str()),
};
let glob: Glob = GlobBuilder::new(pat).literal_separator(true).build()?;
Ok(TouchPattern {
glob: glob.compile_matcher(),
negate,
})
})
.collect()
}
pub fn touch_set_includes(patterns: &[String], path: &str) -> Result<bool, globset::Error> {
let compiled = compile_touch_set(patterns)?;
let mut included = false;
for pat in &compiled {
if pat.glob.is_match(path) {
included = !pat.negate;
}
}
Ok(included)
}
pub struct AttributedChange<'a> {
pub path: &'a str,
pub commit: &'a CommitInfo,
}
pub fn path_findings(touch_set: &[String], changes: &[AttributedChange<'_>]) -> Vec<Finding> {
let mut findings = Vec::new();
let mut seen = std::collections::HashSet::new();
for change in changes {
if !seen.insert(change.path) {
continue;
}
match touch_set_includes(touch_set, change.path) {
Ok(true) => {}
Ok(false) => findings.push(Finding {
subject: change.path.to_string(),
severity: "major".to_string(),
evidence: format!(
"commit {} ({}) touched {} which matches none of the declared touch-set globs",
change.commit.sha, change.commit.subject, change.path
),
suggested_fix: format!(
"relocate the change under a declared touch-set path, or add a glob for {} to the mission's touchSet",
change.path
),
class: FINDING_CLASS.to_string(),
rule: None,
}),
Err(e) => findings.push(Finding {
subject: change.path.to_string(),
severity: "major".to_string(),
evidence: format!(
"touch-set glob compile error while checking {}: {e}",
change.path
),
suggested_fix: "fix the mission's touchSet globs".to_string(),
class: FINDING_CLASS.to_string(),
rule: None,
}),
}
}
findings
}
pub fn primary_checkout_finding(
is_clean: bool,
current_branch: &str,
branch_at_start: &str,
) -> Option<Finding> {
if is_clean && current_branch == branch_at_start {
return None;
}
let evidence = if !is_clean && current_branch != branch_at_start {
format!(
"primary checkout is dirty and moved from '{branch_at_start}' to '{current_branch}'"
)
} else if !is_clean {
"primary checkout has tracked changes while a worktree-mode mission is running".to_string()
} else {
format!("primary checkout moved from '{branch_at_start}' to '{current_branch}'")
};
Some(Finding {
subject: PRIMARY_CHECKOUT_SUBJECT.to_string(),
severity: "critical".to_string(),
evidence,
suggested_fix: "restore the primary checkout to a clean state on the starting branch"
.to_string(),
class: FINDING_CLASS.to_string(),
rule: None,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn commit(sha: &str, subject: &str) -> CommitInfo {
CommitInfo {
sha: sha.to_string(),
subject: subject.to_string(),
}
}
#[test]
fn out_of_contract_path_outside_touch_set_produces_one_finding() {
let touch_set = vec!["src/**".to_string()];
let c = commit("abc123", "[f-1] add");
let changes = [AttributedChange {
path: "docs/oops.md",
commit: &c,
}];
let findings = path_findings(&touch_set, &changes);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].subject, "docs/oops.md");
assert_eq!(findings[0].class, FINDING_CLASS);
assert_eq!(findings[0].severity, "major");
}
#[test]
fn pr_handoff_path_no_longer_flagged_after_touch_set_extension() {
let touch_set = vec![
"crates/engine/src/ticket.rs".to_string(),
"crates/engine/src/types.rs".to_string(),
"crates/engine/src/events.rs".to_string(),
"crates/engine/src/reducer.rs".to_string(),
"crates/engine/src/config.rs".to_string(),
"crates/engine/src/orchestrator.rs".to_string(),
"crates/engine/src/pr_handoff.rs".to_string(),
"crates/engine/tests/reducer_test.rs".to_string(),
];
let c = commit(
"3922d63716f94635c6c343a17cf45198f19be3b1",
"[f-1-2] checkpoint (engine commit)",
);
let changes = [AttributedChange {
path: "crates/engine/src/pr_handoff.rs",
commit: &c,
}];
let findings = path_findings(&touch_set, &changes);
assert!(
findings.is_empty(),
"expected no out-of-contract finding once pr_handoff.rs is declared, got {findings:?}"
);
assert!(touch_set_includes(&touch_set, "crates/engine/src/pr_handoff.rs").unwrap());
}
#[test]
fn slack_outbound_path_no_longer_flagged_after_touch_set_extension() {
let touch_set = vec![
"crates/engine/src/ticket.rs".to_string(),
"crates/engine/src/types.rs".to_string(),
"crates/engine/src/events.rs".to_string(),
"crates/engine/src/reducer.rs".to_string(),
"crates/engine/src/config.rs".to_string(),
"crates/engine/src/orchestrator.rs".to_string(),
"crates/engine/src/pr_handoff.rs".to_string(),
"crates/slack/src/outbound.rs".to_string(),
"crates/engine/tests/reducer_test.rs".to_string(),
];
let c = commit(
"34a0b2fa0f5b88878693c82182381aaeebe5645b",
"[f-1-2] fix workspace build: Mission literal in kranz-slack tests, cargo fmt",
);
let changes = [AttributedChange {
path: "crates/slack/src/outbound.rs",
commit: &c,
}];
let findings = path_findings(&touch_set, &changes);
assert!(
findings.is_empty(),
"expected no out-of-contract finding once outbound.rs is declared, got {findings:?}"
);
assert!(touch_set_includes(&touch_set, "crates/slack/src/outbound.rs").unwrap());
}
#[test]
fn grantable_touch_path_only_for_a_real_out_of_contract_path() {
let touch_set = vec!["src/**".to_string()];
let c = commit("abc123", "[f-1] add");
let real = path_findings(
&touch_set,
&[AttributedChange {
path: "docs/oops.md",
commit: &c,
}],
)
.remove(0);
assert_eq!(
grantable_touch_path(&real, &touch_set),
Some("docs/oops.md")
);
assert_eq!(grantable_touch_path(&real, &["docs/**".to_string()]), None);
let pc = primary_checkout_finding(false, "main", "main").unwrap();
assert_eq!(pc.class, FINDING_CLASS);
assert_eq!(grantable_touch_path(&pc, &touch_set), None);
let bad_set = vec!["[".to_string()];
let glob_err = path_findings(
&bad_set,
&[AttributedChange {
path: "anything.rs",
commit: &c,
}],
)
.remove(0);
assert!(glob_err.evidence.contains("glob compile error"));
assert_eq!(grantable_touch_path(&glob_err, &bad_set), None);
}
#[test]
fn out_of_contract_path_inside_touch_set_produces_no_finding() {
let touch_set = vec!["src/**".to_string()];
let c = commit("abc123", "[f-1] add");
let changes = [AttributedChange {
path: "src/lib.rs",
commit: &c,
}];
assert!(path_findings(&touch_set, &changes).is_empty());
}
#[test]
fn out_of_contract_duplicate_path_produces_exactly_one_finding() {
let touch_set = vec!["src/**".to_string()];
let c1 = commit("aaa", "[f-1] first");
let c2 = commit("bbb", "[f-1] second");
let changes = [
AttributedChange {
path: "docs/oops.md",
commit: &c1,
},
AttributedChange {
path: "docs/oops.md",
commit: &c2,
},
];
let findings = path_findings(&touch_set, &changes);
assert_eq!(findings.len(), 1);
assert!(findings[0].evidence.contains("aaa"));
}
#[test]
fn out_of_contract_negated_glob_excludes_from_touch_set() {
let touch_set = vec!["src/**".to_string(), "!src/generated/**".to_string()];
assert!(touch_set_includes(&touch_set, "src/lib.rs").unwrap());
assert!(!touch_set_includes(&touch_set, "src/generated/x.rs").unwrap());
}
#[test]
fn out_of_contract_empty_touch_set_is_advisory_off() {
let touch_set: Vec<String> = vec![];
assert!(!touch_set_includes(&touch_set, "src/anything.rs").unwrap());
}
#[test]
fn engine_commit_exempt_kranz_prefixed_commit_is_meta() {
assert!(is_meta_commit("[kranz] approved plan for m-abc123"));
assert!(is_meta_commit("[kranz] mission report"));
assert!(is_meta_commit("[kranz] mission report for m-abc123"));
assert!(is_meta_commit("[kranz] revised plan for m-abc123 (rev 2)"));
assert!(!is_meta_commit("[f-1-2] add sweep"));
assert!(!is_meta_commit("[kranz] spoofed worker commit"));
assert!(!is_meta_commit("[kranz]"));
assert!(!is_meta_commit(&checkpoint_commit_message("f-1")));
assert!(!is_meta_commit(¶llel_checkpoint_commit_message("f-1")));
}
#[test]
fn engine_commit_exempt_requires_mission_record_paths_not_just_subject() {
let mission_id = "m-abc123";
assert!(is_meta_commit_with_paths(
"[kranz] approved plan for m-abc123",
mission_id,
&[
".kranz/missions/m-abc123/plan.json".to_string(),
".kranz/missions/m-abc123/plan.md".to_string(),
".kranz/missions/m-abc123/research.md".to_string(),
".kranz/missions/index.md".to_string(),
],
));
assert!(is_meta_commit_with_paths(
"[kranz] revised plan for m-abc123 (rev 2)",
mission_id,
&[".kranz/missions/m-abc123/revised-plan.md".to_string()],
));
assert!(is_meta_commit_with_paths(
"[kranz] mission report for m-abc123",
mission_id,
&[
".kranz/missions/m-abc123/report.md".to_string(),
".kranz/missions/index.md".to_string(),
],
));
for lesson_path in [".kranz/lessons/m-abc123.md", ".kranz/lessons/index.md"] {
assert!(!is_meta_commit_with_paths(
"[kranz] mission report for m-abc123",
mission_id,
&[lesson_path.to_string()],
));
}
assert!(!is_meta_commit_with_paths(
"[kranz] mission report cleanup",
mission_id,
&["src/lib.rs".to_string()],
));
assert!(!is_meta_commit_with_paths(
"[kranz] mission report for m-abc123",
mission_id,
&[
".kranz/missions/m-abc123/report.md".to_string(),
"docs/oops.md".to_string(),
],
));
assert!(!is_meta_commit_with_paths(
"[kranz] mission report for m-abc123",
mission_id,
&[".kranz/missions/m-other/report.md".to_string()],
));
assert!(!is_meta_commit_with_paths(
"[f-1-2] add sweep",
mission_id,
&[".kranz/missions/m-abc123/report.md".to_string()],
));
}
#[test]
fn mission_record_path_matches_engine_commit_sites_only() {
let mission_id = "m-abc123";
for path in [
".kranz/missions/index.md",
".kranz/missions/m-abc123/plan.json",
".kranz/missions/m-abc123/plan.md",
".kranz/missions/m-abc123/revised-plan.md",
".kranz/missions/m-abc123/research.md",
".kranz/missions/m-abc123/report.md",
] {
assert!(is_mission_record_path(mission_id, path), "{path}");
}
for path in [
"src/lib.rs",
".kranz/secret-allowlist",
".kranz/missions/m-abc123", ".kranz/missions/m-abc1234/plan.json", ".kranz/missions/m-other/plan.json",
"kranz/missions/m-abc123/plan.json", ".kranz/missions/m-abc123/arbitrary.rs",
".kranz/missions/m-abc123/nested/plan.json",
".kranz/lessons/index.md",
".kranz/lessons/m-abc123.md", ".kranz/lessons/evil.md", ".kranz/lessons/m-other.md", ".kranz/lessons/nested/index.md",
] {
assert!(!is_mission_record_path(mission_id, path), "{path}");
}
}
#[test]
fn engine_commit_exempt_meta_paths_never_flagged() {
let mission_id = "m-abc123";
assert!(is_meta_path(
mission_id,
".kranz/missions/m-abc123/plan.json"
));
assert!(is_meta_path(mission_id, ".kranz/missions/m-abc123/plan.md"));
assert!(is_meta_path(
mission_id,
".kranz/missions/m-abc123/report.md"
));
assert!(is_meta_path(mission_id, ".kranz/missions/index.md"));
assert!(!is_meta_path(mission_id, "src/lib.rs"));
}
#[test]
fn engine_commit_exempt_kranz_commit_outside_touch_set_produces_no_finding() {
let touch_set = vec!["src/**".to_string()];
let meta_commit = commit("abc123", "[kranz] approved plan for m-abc123");
let worker_commit = commit("def456", "[f-1-2] add sweep");
let all_paths = [
(".kranz/missions/m-abc123/plan.json", &meta_commit),
("src/sweep.rs", &worker_commit),
("docs/oops.md", &worker_commit),
];
let mission_id = "m-abc123";
let filtered: Vec<AttributedChange> = all_paths
.iter()
.filter(|(_, c)| !is_meta_commit(&c.subject))
.filter(|(p, _)| !is_meta_path(mission_id, p))
.map(|(p, c)| AttributedChange { path: p, commit: c })
.collect();
let findings = path_findings(&touch_set, &filtered);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].subject, "docs/oops.md");
}
#[test]
fn primary_checkout_dirty_yields_critical_finding() {
let finding = primary_checkout_finding(false, "main", "main").expect("dirty must flag");
assert_eq!(finding.severity, "critical");
assert_eq!(finding.subject, "primary-checkout");
assert_eq!(finding.class, FINDING_CLASS);
}
#[test]
fn primary_checkout_moved_branch_yields_critical_finding() {
let finding = primary_checkout_finding(true, "kranz/mission-m-abc123", "main")
.expect("moved branch must flag");
assert_eq!(finding.severity, "critical");
assert!(finding.evidence.contains("main"));
}
#[test]
fn primary_checkout_clean_and_unmoved_yields_no_finding() {
assert!(primary_checkout_finding(true, "main", "main").is_none());
}
#[test]
fn anti_vacuity_detects_cargo_test_without_guard() {
assert!(!cargo_test_has_anti_vacuity(
"cargo test --workspace foo 2>&1 | grep -qE 'test result: ok\\.'"
));
assert!(cargo_test_has_anti_vacuity(
"cargo test --workspace foo 2>&1 | grep -qE 'test result: ok\\. [1-9]'"
));
assert!(cargo_test_has_anti_vacuity("npm run test"));
}
}