pub async fn run_code_quality_checks(project_path: &Path) -> CategoryResult {
let mut items = vec![];
let complexity_result = Command::new("pmat")
.args(["analyze", "complexity", "--path"])
.arg(project_path)
.args(["--format", "json"])
.output();
let complexity_status = match complexity_result {
Ok(output) if output.status.success() => {
ValidationStatus::Passed
}
_ => ValidationStatus::Skipped,
};
items.push(ValidationItem {
id: "B1".into(),
description: "Cyclomatic complexity <= 10".into(),
status: complexity_status.clone(),
value: None,
threshold: Some("10".into()),
evidence: None,
});
items.push(ValidationItem {
id: "B2".into(),
description: "Cognitive complexity <= 15".into(),
status: complexity_status,
value: None,
threshold: Some("15".into()),
evidence: None,
});
let clippy_result = Command::new("cargo")
.args(["clippy", "--", "-D", "warnings"])
.current_dir(project_path)
.output();
let clippy_status = match clippy_result {
Ok(output) if output.status.success() => ValidationStatus::Passed,
Ok(_) => ValidationStatus::Failed,
Err(_) => ValidationStatus::Skipped,
};
items.push(ValidationItem {
id: "B5".into(),
description: "No new clippy warnings".into(),
status: clippy_status,
value: None,
threshold: Some("0 warnings".into()),
evidence: None,
});
items.push(ValidationItem {
id: "B3".into(),
description: "Test coverage >= 95%".into(),
status: ValidationStatus::Manual,
value: None,
threshold: Some("95%".into()),
evidence: Some("Run: cargo llvm-cov --html".into()),
});
items.push(ValidationItem {
id: "B4".into(),
description: "Mutation score >= 80%".into(),
status: ValidationStatus::Manual,
value: None,
threshold: Some("80%".into()),
evidence: Some("Run: cargo mutants".into()),
});
let passed = items
.iter()
.filter(|i| i.status == ValidationStatus::Passed)
.count() as u32;
let total = items.len() as u32;
CategoryResult {
name: "Code Quality".into(),
passed,
total,
items,
}
}
pub async fn run_testing_checks(project_path: &Path) -> CategoryResult {
let mut items = vec![];
let test_result = Command::new("cargo")
.args(["test", "--", "--test-threads=1"])
.current_dir(project_path)
.output();
let test_status = match test_result {
Ok(output) if output.status.success() => ValidationStatus::Passed,
Ok(_) => ValidationStatus::Failed,
Err(_) => ValidationStatus::Skipped,
};
items.push(ValidationItem {
id: "C1".into(),
description: "Unit tests passing".into(),
status: test_status,
value: None,
threshold: Some("All pass".into()),
evidence: None,
});
items.push(ValidationItem {
id: "C2".into(),
description: "Unit tests cover error paths".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: Some("Review test coverage for error handling".into()),
});
items.push(ValidationItem {
id: "C3".into(),
description: "Property tests for complex logic".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: Some("Check for proptest usage".into()),
});
items.push(ValidationItem {
id: "C4".into(),
description: "Integration tests for API boundaries".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "C5".into(),
description: "Golden tests for output formats".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
let passed = items
.iter()
.filter(|i| i.status == ValidationStatus::Passed)
.count() as u32;
let total = items.len() as u32;
CategoryResult {
name: "Testing".into(),
passed,
total,
items,
}
}
pub async fn run_documentation_checks(project_path: &Path, task_id: &str) -> CategoryResult {
let mut items = vec![];
let changelog_path = project_path.join("CHANGELOG.md");
let changelog_status = if changelog_path.exists() {
let content = fs::read_to_string(&changelog_path).unwrap_or_default();
if content.contains(task_id) || content.contains("Unreleased") {
ValidationStatus::Passed
} else {
ValidationStatus::Warning
}
} else {
ValidationStatus::Skipped
};
items.push(ValidationItem {
id: "D3".into(),
description: "CHANGELOG updated".into(),
status: changelog_status,
value: None,
threshold: None,
evidence: None,
});
let doc_result = Command::new("cargo")
.args(["doc", "--no-deps"])
.current_dir(project_path)
.output();
let doc_status = match doc_result {
Ok(output) if output.status.success() => ValidationStatus::Passed,
Ok(_) => ValidationStatus::Warning,
Err(_) => ValidationStatus::Skipped,
};
items.push(ValidationItem {
id: "D1".into(),
description: "Public API documented".into(),
status: doc_status,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "D2".into(),
description: "Examples provided in docs".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "D4".into(),
description: "README reflects changes".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "D5".into(),
description: "Error messages are actionable".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
let passed = items
.iter()
.filter(|i| i.status == ValidationStatus::Passed)
.count() as u32;
let total = items.len() as u32;
CategoryResult {
name: "Documentation".into(),
passed,
total,
items,
}
}
pub async fn run_process_checks(project_path: &Path, task_id: &str) -> CategoryResult {
let mut items = vec![];
let git_result = Command::new("git")
.args(["log", "--oneline", "-10"])
.current_dir(project_path)
.output();
let commit_status = match git_result {
Ok(output) if output.status.success() => {
let log = String::from_utf8_lossy(&output.stdout);
if log.contains(task_id) || log.contains(&format!("#{}", task_id)) {
ValidationStatus::Passed
} else {
ValidationStatus::Warning
}
}
_ => ValidationStatus::Skipped,
};
items.push(ValidationItem {
id: "E2".into(),
description: "Commit messages reference ticket".into(),
status: commit_status,
value: None,
threshold: None,
evidence: Some(format!("Checked for: {}", task_id)),
});
items.push(ValidationItem {
id: "E4".into(),
description: "CI/CD passes all gates".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: Some("Check GitHub Actions".into()),
});
items.push(ValidationItem {
id: "E1".into(),
description: "All acceptance criteria met".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "E3".into(),
description: "PR description complete".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
items.push(ValidationItem {
id: "E5".into(),
description: "Peer review completed".into(),
status: ValidationStatus::Manual,
value: None,
threshold: None,
evidence: None,
});
let passed = items
.iter()
.filter(|i| i.status == ValidationStatus::Passed)
.count() as u32;
let total = items.len() as u32;
CategoryResult {
name: "Process".into(),
passed,
total,
items,
}
}